Introduction
#
Materials and setup
Lab computer users: Log in using the user name and password on the board to your left.
Laptop users: You should have R installed –if not:
Open a web browser and go to http://cran.r-project.org and download and install it
Also helpful to install RStudio (download from http://rstudio.com)
In R, type install.packages("ggplot2") to install the ggplot2 package.
Everyone: Download workshop materials:
Workshop notes are available in .hmtl format. Open a file browser, navigate to your desktop and open Rgraphics.html
Workshop Overview
Class Structure and Organization:
- Ask questions at any time. Really!
- Collaboration is encouraged
- This is your class! Special requests are encouraged
This is an intermediate R course:
- Assumes working knowledge of R
- Relatively fast-paced
- Focus is on
ggplot2 graphics–other packages will not be covered
Starting A The End
My goal: by the end of the workshop you will be able to reproduce this graphic from the Economist:
Why ggplot2?
Advantages of ggplot2
- consistent underlying
grammar of graphics (Wilkinson, 2005)
- plot specification at a high level of abstraction
- very flexible
- theme system for polishing plot appearance
- mature and complete graphics system
- many users, active mailing list
That said, there are some things you cannot (or should not) do With ggplot2:
- 3-dimensional graphics (see the rgl package)
- Graph-theory type graphs (nodes/edges layout; see the igraph package)
- Interactive graphics (see the ggvis package)
What Is The Grammar Of Graphics?
The basic idea: independently specify plot building blocks and combine them to create just about any kind of graphical display you want. Building blocks of a graph include:
- data
- aesthetic mapping
- geometric object
- statistical transformations
- scales
- coordinate system
- position adjustments
- faceting
The structure of a ggplot
The ggplot() function is used to initialize the basic graph structure, then we add to it. The structure of a ggplot looks like this:
# ggplot(data = <default data set>,
# aes(x = <default x axis variable>,
# y = <default y axis variable>,
# ... <other default aesthetic mappings>),
# ... <other plot defaults>) +
#
# geom_<geom type>(aes(size = <size variable for this geom>,
# ... <other aesthetic mappings>),
# data = <data for this point geom>,
# stat = <statistic string or function>,
# position = <position string or function>,
# color = <"fixed color specification">,
# <other arguments, possibly passed to the _stat_ function) +
#
# scale_<aesthetic>_<type>(name = <"scale label">,
# breaks = <where to put tick marks>,
# labels = <labels for tick marks>,
# ... <other options for the scale>) +
#
# theme(plot.background = element_rect(fill = "gray"),
# ... <other theme elements>)
Don’t be afraid, you will understand this by the end of the workshop! The basic idea is that you specify different parts of the plot, and add them together using the + operator.
ggplot2 VS Base Graphics
Compared to base graphics, ggplot2
- is more verbose for simple / canned graphics
- is less verbose for complex / custom graphics
- does not have methods (data should always be in a
data.frame)
- uses a different system for adding plot elements
ggplot2 VS Base for simple graphs
Base graphics histogram example:
hist(housing$Home.Value)

ggplot2 histogram example:
library(ggplot2)
ggplot(housing, aes(x = Home.Value)) +
geom_histogram()

Base wins!
ggplot2 Base graphics VS ggplot for more complex graphs:
Base colored scatter plot example:
plot(Home.Value ~ Date,
data=subset(housing, State == "MA"))
points(Home.Value ~ Date, col="red",
data=subset(housing, State == "TX"))
legend(19750, 400000,
c("MA", "TX"), title="State",
col=c("black", "red"),
pch=c(1, 1))

ggplot2 colored scatter plot example:
ggplot(subset(housing, State %in% c("MA", "TX")),
aes(x=Date,
y=Home.Value,
color=State))+
geom_point()

ggplot2 wins!
Geometric Objects And Aesthetics
Aesthetic Mapping
In ggplot land aesthetic means “something you can see”. Examples include:
- position (i.e., on the x and y axes)
- color (“outside” color)
- fill (“inside” color)
- shape (of points)
- linetype
- size
Each type of geom accepts only a subset of all aesthetics–refer to the geom help pages to see what mappings each geom accepts. Aesthetic mappings are set with the aes() function.
Geometic Objects (geom)
Geometric objects are the actual marks we put on a plot. Examples include:
- points (
geom_point, for scatter plots, dot plots, etc)
- lines (
geom_line, for time series, trend lines, etc)
- boxplot (
geom_boxplot, for, well, boxplots!)
A plot must have at least one geom; there is no upper limit. You can add a geom to a plot using the + operator
You can get a list of available geometric objects using the code below:
help.search("geom_", package = "ggplot2")
or simply type geom_<tab> in any good R IDE (such as Rstudio or ESS) to see a list of functions starting with geom_.
Points (Scatterplot)
Now that we know about geometric objects and aesthetic mapping, we can make a ggplot. geom_point requires mappings for x and y, all others are optional.
hp2001Q1 <- subset(housing, Date == 20011)
ggplot(hp2001Q1,
aes(y = Structure.Cost, x = Land.Value)) +
geom_point()

Lines (Prediction Line)
A plot constructed with ggplot can have more than one geom. In that case the mappings established in the ggplot() call are plot defaults that can be added to or overridden. Our plot could use a regression line:
hp2001Q1$pred.SC <- predict(lm(Structure.Cost ~ Land.Value, data = hp2001Q1))
p1 <- ggplot(hp2001Q1, aes(x = Land.Value, y = Structure.Cost))
p1 + geom_point(aes(color = Home.Value)) +
geom_line(aes(y = pred.SC))

Smoothers
Not all geometric objects are simple shapes–the smooth geom includes a line and a ribbon.
p1 +
geom_point(aes(color = Home.Value)) +
geom_smooth()

Text (Label Points)
Each geom accepts a particualar set of mappings–for example geom_text() accepts a labels mapping.
p1 +
geom_text(aes(label=State), size = 3)

## install.packages("ggrepel")
library("ggrepel")
p1 +
geom_point() +
geom_text_repel(aes(label=State), size = 3)

Aesthetic Mapping VS Assignment
Note that variables are mapped to aesthetics with the aes() function, while fixed aesthetics are set outside the aes() call. This sometimes leads to confusion, as in this example:
p1 +
geom_point(aes(size = 2),# incorrect! 2 is not a variable
color="red") # this is fine -- all points red

Mapping Variables To Other Aesthetics
Other aesthetics are mapped in the same way as x and y in the previous example.
p1 +
geom_point(aes(color=Home.Value, shape = region))

Exercise I prototype
- \[@1\] Create a scatter plot with CPI on the x axis and HDI on the y axis.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point()

- \[@2\] Color the points in the previous plot blue.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point(color = "blue")

- \[@3\] Color the points in the previous plot according to Region.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region))

- \[@4\] Create boxplots of CPI by Region
ggplot(dat, aes(x = Region, y = CPI)) +
geom_boxplot()

- \[@5\] Overlay points on top of the box plots
ggplot(dat, aes(x = Region, y = CPI)) +
geom_boxplot() +
geom_point()

Statistical Transformations
Exercise II
- Re-create a scatter plot with CPI on the x axis and HDI on the y axis (as you did in the previous exercise).
- Overlay a smoothing line on top of the scatter plot using the lm method. Hint: see
?stat_smooth.
- Overlay a smoothing line on top of the scatter plot using the default method.
- BONUS (optional): Overlay a smoothing line on top of the scatter plot using the default loess method, but make it less smooth. Hint: see
?loess.
Exercise II prototype
- \[@1\] Re-create a scatter plot with CPI on the x axis and HDI on the y axis (as you did in the previous exercise).
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point()

- \[@2\] Overlay a smoothing line on top of the scatter plot using the lm method. Hint: see
?stat_smooth.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth(method = "lm")

- \[@3\] Overlay a smoothing line on top of the scatter plot using the default method.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth()

- \[@4\] BONUS (optional): Overlay a smoothing line on top of the scatter plot using the default loess method, but make it less smooth. Hint: see
?loess.
ggplot(dat, aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth(span = .4)

Scales
Scales: Controlling Aesthetic Mapping
Aesthetic mapping (i.e., with aes()) only says that a variable should be mapped to an aesthetic. It doesn’t say how that should happy. For example, when mapping a variable to shape with aes(shape x)= you don’t say what shapes should be used. Similarly, aes(color z)= doesn’t say what colors should be used. Describing what colors/shapes/sizes etc. to use is done by modifying the corresponding scale. In ggplot2 scales include
- position
- color and fill
- size
- shape
- line type
Scales are modified with a series of functions using a scale_<aesthetic>_<type> naming scheme. Try typing scale_<tab> to see a list of scale modification functions.
Common Scale Arguments
The following arguments are common to most scales in ggplot2:
name
the first argument gives the axis or legend title
limits
the minimum and maximum of the scale
breaks
the points along the scale where labels should appear
labels
the labels that appear at each break
Specific scale functions may have additional arguments; for example, the scale_color_continuous function has arguments low and high for setting the colors at the low and high end of the scale.
Scale Modification Examples
Start by constructing a dotplot showing the distribution of home values by Date and State.
p3 <- ggplot(housing,
aes(x = State,
y = Home.Price.Index)) +
theme(legend.position="top",
axis.text=element_text(size = 6))
(p4 <- p3 + geom_point(aes(color = Date),
alpha = 0.5,
size = 1.5,
position = position_jitter(width = 0.25, height = 0)))

Now modify the breaks and labels for the x axis and color scales
p4 + scale_x_discrete(name="State Abbreviation") +
scale_color_continuous(name="",
breaks = c(19751, 19941, 20131),
labels = c(1971, 1994, 2013))

Next change the low and high values to blue and red:
p4 +
scale_x_discrete(name="State Abbreviation") +
scale_color_continuous(name="",
breaks = c(19751, 19941, 20131),
labels = c(1971, 1994, 2013),
low = "blue", high = "red")

library(scales) # Para usar la función muted
p4 +
scale_color_continuous(name="",
breaks = c(19751, 19941, 20131),
labels = c(1971, 1994, 2013),
low = muted("blue"), high = muted("red"))

Using different color scales
ggplot2 has a wide variety of color scales; here is an example using scale_color_gradient2 to interpolate between three different colors.
library(scales)
p4 +
scale_color_gradient2(name="",
breaks = c(19751, 19941, 20131),
labels = c(1971, 1994, 2013),
low = muted("blue"),
high = muted("red"),
mid = "gray60",
midpoint = 19941)

Available Scales
- Partial combination matrix of available scales
| scalecolor_ |
identity |
scalefillcontinuous |
| scalefill_ |
manual |
scalecolordiscrete |
| scalesize_ |
continuous |
scalesizemanual |
|
discrete |
scalesizediscrete |
| scaleshape_ |
discrete |
scaleshapediscrete |
| scalelinetype_ |
identity |
scaleshapemanual |
|
manual |
scalelinetypediscrete |
| scalex_ |
continuous |
scalexcontinuous |
| scaley_ |
discrete |
scaleydiscrete |
|
reverse |
scalexlog |
|
log |
scaleyreverse |
|
date |
scalexdate |
|
datetime |
scaleydatetime |
Note that in RStudio you can type scale_ followed by TAB to get the whole list of available scales.
Exercise III
- Create a scatter plot with CPI on the x axis and HDI on the y axis. Color the points to indicate region.
- Modify the x, y, and color scales so that they have more easily-understood names (e.g., spell out “Human development Index” instead of “HDI”).
- Modify the color scale to use specific values of your choosing. Hint: see
?scale_color_manual.
Exercise III prototype
- \[@1\] Create a scatter plot with CPI on the x axis and HDI on the y axis. Color the points to indicate region.
ggplot(dat, aes(x = CPI, y = HDI, color = "Region")) +
geom_point()

- \[@2\] Modify the x, y, and color scales so that they have more easily-understood names (e.g., spell out “Human development Index” instead of “HDI”).
ggplot(dat, aes(x = CPI, y = HDI, color = "Region")) +
geom_point() +
scale_x_continuous(name = "Corruption Perception Index") +
scale_y_continuous(name = "Human Development Index") +
scale_color_discrete(name = "Region of the world")

- \[@3\] Modify the color scale to use specific values of your choosing. Hint: see
?scale_color_manual.
ggplot(dat, aes(x = CPI, y = HDI, color = "Region")) +
geom_point() +
scale_x_continuous(name = "Corruption Perception Index") +
scale_y_continuous(name = "Human Development Index") +
scale_color_manual(name = "Region of the world",
values = c("#24576D",
"#099DD7",
"#28AADC",
"#248E84",
"#F2583F",
"#96503F"))

Challenge Solution
Lets start by creating the basic scatter plot, then we can make a list of things that need to be added or changed. The basic plot loogs like this:
dat <- read.csv("dataSets/EconomistData.csv")
pc1 <- ggplot(dat, aes(x = CPI, y = HDI, color = Region))
pc1 + geom_point()

To complete this graph we need to:
- \[ \] add a trend line
- \[ \] change the point shape to open circle
- \[ \] change the order and labels of Region
- \[ \] label select points
- \[ \] fix up the tick marks and labels
- \[ \] move color legend to the top
- \[ \] title, label axes, remove legend title
- \[ \] theme the graph with no vertical guides
- \[ \] add model R2 (hard)
- \[ \] add sources note (hard)
- \[ \] final touches to make it perfect (use image editor for this)
Adding the trend line
Adding the trend line is not too difficult, though we need to guess at the model being displyed on the graph. A little bit of trial and error leds to
(pc2 <- pc1 +
geom_smooth(aes(group = 1),
method = "lm",
formula = y ~ log(x),
se = FALSE,
color = "red")) +
geom_point()

Notice that we put the geom_line layer first so that it will be plotted underneath the points, as was done on the original graph.
Use open points
This one is a little tricky. We know that we can change the shape with the shape argument, what what value do we set shape to? The example shown in ?shape can help us:
## A look at all 25 symbols
df2 <- data.frame(x = 1:5 , y = 1:25, z = 1:25)
s <- ggplot(df2, aes(x = x, y = y))
s + geom_point(aes(shape = z), size = 4) + scale_shape_identity()

## While all symbols have a foreground colour, symbols 19-25 also take a
## background colour (fill)
s + geom_point(aes(shape = z), size = 4, colour = "Red") +
scale_shape_identity()

s + geom_point(aes(shape = z), size = 4, colour = "Red", fill = "Black") +
scale_shape_identity()

This shows us that shape 1 is an open circle, so
pc2 +
geom_point(shape = 1, size = 4)

That is better, but unfortunately the size of the line around the points is much narrower than on the original. This is a frustrating aspect of ggplot2, and we will have to hack around it. One way to do that is to multiple point layers of slightly different sizes.
(pc3 <- pc2 +
geom_point(size = 4.5, shape = 1) +
geom_point(size = 4, shape = 1) +
geom_point(size = 3.5, shape = 1))

Labelling points
This one is tricky in a couple of ways. First, there is no attribute in the data that separates points that should be labelled from points that should not be. So the first step is to identify those points.
pointsToLabel <- c("Russia", "Venezuela", "Iraq", "Myanmar", "Sudan",
"Afghanistan", "Congo", "Greece", "Argentina", "Brazil",
"India", "Italy", "China", "South Africa", "Spane",
"Botswana", "Cape Verde", "Bhutan", "Rwanda", "France",
"United States", "Germany", "Britain", "Barbados", "Norway", "Japan",
"New Zealand", "Singapore")
Now we can label these points using geom_text, like this:
#+ENDSRC
(pc4 <- pc3 +
geom_text(aes(label = Country),
color = "gray20",
data = subset(dat, Country %in% pointsToLabel)))

This more or less gets the information across, but the labels overlap in a most unpleasing fashion. We can use the ggrepel package to make things better, but if you want perfection you will probably have to do some hand-adjustment.
library("ggrepel")
pc3 +
geom_text_repel(aes(label = Country),
color = "gray20",
data = subset(dat, Country %in% pointsToLabel),
force = 10)

Change the region labels and order
Thinkgs are starting to come together. There are just a couple more things we need to add, and then all that will be left are themeing changes.
Comparing our graph to the original we notice that the labels and order of the Regions in the color legend differ. To correct this we need to change both the labels and order of the Region variable. We can do this with the factor function.
dat$Region <- factor(dat$Region,
levels = c("EU W. Europe",
"Americas",
"Asia Pacific",
"East EU Cemt Asia",
"MENA",
"SSA"),
labels = c("OECD",
"Americas",
"Asia &\nOceania",
"Central &\nEastern Europe",
"Middle East &\nnorth Africa",
"Sub-Saharan\nAfrica"))
Now when we construct the plot using these data the order should appear as it does in the original.
pc4$data <- dat
pc4

Theme tweaks
Our graph is almost there. To finish up, we need to adjust some of the theme elements, and label the axes and legends. This part usually involves some trial and error as you figure out where things need to be positioned. To see what these various theme settings do you can change them and observe the results.
library(grid) # for the 'unit' function
(pc6 <- pc5 +
theme_minimal() + # start with a minimal theme and add what we need
theme(text = element_text(color = "gray20"),
legend.position = c("top"), # position the legend in the upper left
legend.direction = "horizontal",
legend.justification = 0.1, # anchor point for legend.position.
legend.text = element_text(size = 11, color = "gray10"),
axis.text = element_text(face = "italic"),
axis.title.x = element_text(vjust = -1), # move title away from axis
axis.title.y = element_text(vjust = 2), # move away for axis
axis.ticks.y = element_blank(), # element_blank() is how we remove elements
axis.line = element_line(color = "gray40", size = 0.5),
axis.line.y = element_blank(),
panel.grid.major = element_line(color = "gray50", size = 0.5),
panel.grid.major.x = element_blank()
))

Add model R2 and source note
The last bit of information that we want to have on the graph is the variance explained by the model represented by the trend line. Lets fit that model and pull out the R2 first, then think about how to get it onto the graph.
(mR2 <- summary(lm(HDI ~ log(CPI), data = dat))$r.squared)
[1] 0.5212859
OK, now that we’ve calculated the values, let’s think about how to get them on the graph. ggplot2 has an annotate function, but this is not convenient for adding elements outside the plot area. The grid package has nice functions for doing this, so we’ll use those.
And here it is, our final version!
library(grid)
png(file = "images/econScatter10.png", width = 800, height = 600)
pc6
grid.text("Sources: Transparency International; UN Human Development Report",
x = .02, y = .03,
just = "left",
draw = TRUE)
grid.segments(x0 = 0.81, x1 = 0.825,
y0 = 0.90, y1 = 0.90,
gp = gpar(col = "red"),
draw = TRUE)
grid.text(paste0("R² = ",
as.integer(mR2*100),
"%"),
x = 0.835, y = 0.90,
gp = gpar(col = "gray20"),
draw = TRUE,
just = "left")
dev.off()
null device
1
Comparing it to the original suggests that we’ve got most of the important elements, though of course the two graphs are not identical. 
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKSW50cm9kdWN0aW9uCj09PT09PT09PT09PQoKIzxkaXYgY2xhc3M9Im1hdGVyaWFscy1uby1pcHluYiI+CgpNYXRlcmlhbHMgYW5kIHNldHVwCi0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyBMYWIgY29tcHV0ZXIgdXNlcnM6IExvZyBpbiB1c2luZyB0aGUgdXNlciBuYW1lIGFuZCBwYXNzd29yZCBvbiB0aGUgYm9hcmQgdG8geW91ciBsZWZ0LjxzcGFuIGNsYXNzPSJ0YWciIGRhdGEtdGFnLW5hbWU9ImxhYnNldHVwIj48L3NwYW4+CgojIyMgTGFwdG9wIHVzZXJzOiBZb3Ugc2hvdWxkIGhhdmUgUiBpbnN0YWxsZWQgLS1pZiBub3Q6CgojIyMjIE9wZW4gYSB3ZWIgYnJvd3NlciBhbmQgZ28gdG8gPGh0dHA6Ly9jcmFuLnItcHJvamVjdC5vcmc+IGFuZCBkb3dubG9hZCBhbmQgaW5zdGFsbCBpdAoKIyMjIyBBbHNvIGhlbHBmdWwgdG8gaW5zdGFsbCBSU3R1ZGlvIChkb3dubG9hZCBmcm9tIDxodHRwOi8vcnN0dWRpby5jb20+KQoKIyMjIyBJbiBSLCB0eXBlIGBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIilgIHRvIGluc3RhbGwgdGhlIGdncGxvdDIgcGFja2FnZS4KCiMjIyBFdmVyeW9uZTogRG93bmxvYWQgd29ya3Nob3AgbWF0ZXJpYWxzOgoKIyMjIyBEb3dubG9hZCBtYXRlcmlhbHMgZnJvbSA8aHR0cDovL3R1dG9yaWFscy5pcS5oYXJ2YXJkLmVkdS9SL1JncmFwaGljcy56aXA+CgojIyMjIEV4dHJhY3QgdGhlIHppcCBmaWxlIGNvbnRhaW5pbmcgdGhlIG1hdGVyaWFscyB0byB5b3VyIGRlc2t0b3AKCldvcmtzaG9wIG5vdGVzIGFyZSBhdmFpbGFibGUgaW4gLmhtdGwgZm9ybWF0LiBPcGVuIGEgZmlsZSBicm93c2VyLCBuYXZpZ2F0ZSB0byB5b3VyIGRlc2t0b3AgYW5kIG9wZW4gUmdyYXBoaWNzLmh0bWwKCldvcmtzaG9wIE92ZXJ2aWV3Ci0tLS0tLS0tLS0tLS0tLS0tCgpDbGFzcyBTdHJ1Y3R1cmUgYW5kIE9yZ2FuaXphdGlvbjoKCi0gICBBc2sgcXVlc3Rpb25zIGF0IGFueSB0aW1lLiBSZWFsbHkhCi0gICBDb2xsYWJvcmF0aW9uIGlzIGVuY291cmFnZWQKLSAgIFRoaXMgaXMgeW91ciBjbGFzcyEgU3BlY2lhbCByZXF1ZXN0cyBhcmUgZW5jb3VyYWdlZAoKPC9kaXY+CgpUaGlzIGlzIGFuIGludGVybWVkaWF0ZSBSIGNvdXJzZToKCi0gICBBc3N1bWVzIHdvcmtpbmcga25vd2xlZGdlIG9mIFIKLSAgIFJlbGF0aXZlbHkgZmFzdC1wYWNlZAotICAgRm9jdXMgaXMgb24gYGdncGxvdDJgIGdyYXBoaWNzLS1vdGhlciBwYWNrYWdlcyB3aWxsIG5vdCBiZSBjb3ZlcmVkCgpTdGFydGluZyBBIFRoZSBFbmQKLS0tLS0tLS0tLS0tLS0tLS0tCgpNeSBnb2FsOiBieSB0aGUgZW5kIG9mIHRoZSB3b3Jrc2hvcCB5b3Ugd2lsbCBiZSBhYmxlIHRvIHJlcHJvZHVjZSB0aGlzIGdyYXBoaWMgZnJvbSB0aGUgRWNvbm9taXN0OgoKIVtdKGltYWdlcy9FY29ub21pc3QxLnBuZykKCldoeSBgZ2dwbG90MmA/Ci0tLS0tLS0tLS0tLS0tCgpBZHZhbnRhZ2VzIG9mIGdncGxvdDIKCi0gICBjb25zaXN0ZW50IHVuZGVybHlpbmcgYGdyYW1tYXIgb2YgZ3JhcGhpY3NgIChXaWxraW5zb24sIDIwMDUpCi0gICBwbG90IHNwZWNpZmljYXRpb24gYXQgYSBoaWdoIGxldmVsIG9mIGFic3RyYWN0aW9uCi0gICB2ZXJ5IGZsZXhpYmxlCi0gICB0aGVtZSBzeXN0ZW0gZm9yIHBvbGlzaGluZyBwbG90IGFwcGVhcmFuY2UKLSAgIG1hdHVyZSBhbmQgY29tcGxldGUgZ3JhcGhpY3Mgc3lzdGVtCi0gICBtYW55IHVzZXJzLCBhY3RpdmUgbWFpbGluZyBsaXN0CgpUaGF0IHNhaWQsIHRoZXJlIGFyZSBzb21lIHRoaW5ncyB5b3UgY2Fubm90IChvciBzaG91bGQgbm90KSBkbyBXaXRoIGdncGxvdDI6CgotICAgMy1kaW1lbnNpb25hbCBncmFwaGljcyAoc2VlIHRoZSByZ2wgcGFja2FnZSkKLSAgIEdyYXBoLXRoZW9yeSB0eXBlIGdyYXBocyAobm9kZXMvZWRnZXMgbGF5b3V0OyBzZWUgdGhlIGlncmFwaCBwYWNrYWdlKQotICAgSW50ZXJhY3RpdmUgZ3JhcGhpY3MgKHNlZSB0aGUgZ2d2aXMgcGFja2FnZSkKCldoYXQgSXMgVGhlIEdyYW1tYXIgT2YgR3JhcGhpY3M/Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpUaGUgYmFzaWMgaWRlYTogaW5kZXBlbmRlbnRseSBzcGVjaWZ5IHBsb3QgYnVpbGRpbmcgYmxvY2tzIGFuZCBjb21iaW5lIHRoZW0gdG8gY3JlYXRlIGp1c3QgYWJvdXQgYW55IGtpbmQgb2YgZ3JhcGhpY2FsIGRpc3BsYXkgeW91IHdhbnQuIEJ1aWxkaW5nIGJsb2NrcyBvZiBhIGdyYXBoIGluY2x1ZGU6CgotICAgZGF0YQotICAgYWVzdGhldGljIG1hcHBpbmcKLSAgIGdlb21ldHJpYyBvYmplY3QKLSAgIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9ucwotICAgc2NhbGVzCi0gICBjb29yZGluYXRlIHN5c3RlbQotICAgcG9zaXRpb24gYWRqdXN0bWVudHMKLSAgIGZhY2V0aW5nCgpUaGUgc3RydWN0dXJlIG9mIGEgYGdncGxvdGAKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpUaGUgYGdncGxvdCgpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIGluaXRpYWxpemUgdGhlIGJhc2ljIGdyYXBoIHN0cnVjdHVyZSwgdGhlbiB3ZSBhZGQgdG8gaXQuIFRoZSBzdHJ1Y3R1cmUgb2YgYSBnZ3Bsb3QgbG9va3MgbGlrZSB0aGlzOgoKYGBgIHtyfQogICMgZ2dwbG90KGRhdGEgPSA8ZGVmYXVsdCBkYXRhIHNldD4sIAogICMgICAgICAgIGFlcyh4ID0gPGRlZmF1bHQgeCBheGlzIHZhcmlhYmxlPiwKICAjICAgICAgICAgICAgeSA9IDxkZWZhdWx0IHkgYXhpcyB2YXJpYWJsZT4sCiAgIyAgICAgICAgICAgIC4uLiA8b3RoZXIgZGVmYXVsdCBhZXN0aGV0aWMgbWFwcGluZ3M+KSwKICAjICAgICAgICAuLi4gPG90aGVyIHBsb3QgZGVmYXVsdHM+KSArCiAgIyAKICAjICAgICAgICBnZW9tXzxnZW9tIHR5cGU+KGFlcyhzaXplID0gPHNpemUgdmFyaWFibGUgZm9yIHRoaXMgZ2VvbT4sIAogICMgICAgICAgICAgICAgICAgICAgICAgIC4uLiA8b3RoZXIgYWVzdGhldGljIG1hcHBpbmdzPiksCiAgIyAgICAgICAgICAgICAgICAgICBkYXRhID0gPGRhdGEgZm9yIHRoaXMgcG9pbnQgZ2VvbT4sCiAgIyAgICAgICAgICAgICAgICAgICBzdGF0ID0gPHN0YXRpc3RpYyBzdHJpbmcgb3IgZnVuY3Rpb24+LAogICMgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSA8cG9zaXRpb24gc3RyaW5nIG9yIGZ1bmN0aW9uPiwKICAjICAgICAgICAgICAgICAgICAgIGNvbG9yID0gPCJmaXhlZCBjb2xvciBzcGVjaWZpY2F0aW9uIj4sCiAgIyAgICAgICAgICAgICAgICAgICA8b3RoZXIgYXJndW1lbnRzLCBwb3NzaWJseSBwYXNzZWQgdG8gdGhlIF9zdGF0XyBmdW5jdGlvbikgKwogICMgCiAgIyAgIHNjYWxlXzxhZXN0aGV0aWM+Xzx0eXBlPihuYW1lID0gPCJzY2FsZSBsYWJlbCI+LAogICMgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gPHdoZXJlIHRvIHB1dCB0aWNrIG1hcmtzPiwKICAjICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IDxsYWJlbHMgZm9yIHRpY2sgbWFya3M+LAogICMgICAgICAgICAgICAgICAgICAgICAgLi4uIDxvdGhlciBvcHRpb25zIGZvciB0aGUgc2NhbGU+KSArCiAgIyAKICAjICAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheSIpLAogICMgICAgICAgICAuLi4gPG90aGVyIHRoZW1lIGVsZW1lbnRzPikKYGBgCgpEb24ndCBiZSBhZnJhaWQsIHlvdSB3aWxsIHVuZGVyc3RhbmQgdGhpcyBieSB0aGUgZW5kIG9mIHRoZSB3b3Jrc2hvcCEgVGhlIGJhc2ljIGlkZWEgaXMgdGhhdCB5b3Ugc3BlY2lmeSBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIHBsb3QsIGFuZCBhZGQgdGhlbSB0b2dldGhlciB1c2luZyB0aGUgYCtgIG9wZXJhdG9yLgoKRXhhbXBsZSBEYXRhOiBgSG91c2luZyBwcmljZXNgCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKTGV0J3MgbG9vayBhdCBob3VzaW5nIHByaWNlcy4KCmBgYCB7cn0KICBob3VzaW5nIDwtIHJlYWQuY3N2KCJkYXRhU2V0cy9sYW5kZGF0YS1zdGF0ZXMuY3N2IikKICBoZWFkKGhvdXNpbmdbMTo1XSkKYGBgCgooRGF0YSBmcm9tIDxodHRwczovL3d3dy5saW5jb2xuaW5zdC5lZHUvc3ViY2VudGVycy9sYW5kLXZhbHVlcy9sYW5kLXByaWNlcy1ieS1zdGF0ZS5hc3A+KQoKYGdncGxvdDJgIFZTIEJhc2UgR3JhcGhpY3MKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkNvbXBhcmVkIHRvIGJhc2UgZ3JhcGhpY3MsIGBnZ3Bsb3QyYAoKLSAgIGlzIG1vcmUgdmVyYm9zZSBmb3Igc2ltcGxlIC8gY2FubmVkIGdyYXBoaWNzCi0gICBpcyBsZXNzIHZlcmJvc2UgZm9yIGNvbXBsZXggLyBjdXN0b20gZ3JhcGhpY3MKLSAgIGRvZXMgbm90IGhhdmUgbWV0aG9kcyAoZGF0YSBzaG91bGQgYWx3YXlzIGJlIGluIGEgYGRhdGEuZnJhbWVgKQotICAgdXNlcyBhIGRpZmZlcmVudCBzeXN0ZW0gZm9yIGFkZGluZyBwbG90IGVsZW1lbnRzCgpgZ2dwbG90MmAgVlMgQmFzZSBmb3Igc2ltcGxlIGdyYXBocwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQmFzZSBncmFwaGljcyBoaXN0b2dyYW0gZXhhbXBsZToKCmBgYCB7cn0KICBoaXN0KGhvdXNpbmckSG9tZS5WYWx1ZSkKYGBgCgpgZ2dwbG90MmAgaGlzdG9ncmFtIGV4YW1wbGU6CgpgYGAge3J9CiAgbGlicmFyeShnZ3Bsb3QyKQogIGdncGxvdChob3VzaW5nLCBhZXMoeCA9IEhvbWUuVmFsdWUpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKQmFzZSB3aW5zIQoKYGdncGxvdDJgIEJhc2UgZ3JhcGhpY3MgVlMgYGdncGxvdGAgZm9yIG1vcmUgY29tcGxleCBncmFwaHM6Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQmFzZSBjb2xvcmVkIHNjYXR0ZXIgcGxvdCBleGFtcGxlOgoKYGBgIHtyfQogIHBsb3QoSG9tZS5WYWx1ZSB+IERhdGUsCiAgICAgICBkYXRhPXN1YnNldChob3VzaW5nLCBTdGF0ZSA9PSAiTUEiKSkKICBwb2ludHMoSG9tZS5WYWx1ZSB+IERhdGUsIGNvbD0icmVkIiwKICAgICAgICAgZGF0YT1zdWJzZXQoaG91c2luZywgU3RhdGUgPT0gIlRYIikpCiAgbGVnZW5kKDE5NzUwLCA0MDAwMDAsCiAgICAgICAgIGMoIk1BIiwgIlRYIiksIHRpdGxlPSJTdGF0ZSIsCiAgICAgICAgIGNvbD1jKCJibGFjayIsICJyZWQiKSwKICAgICAgICAgcGNoPWMoMSwgMSkpCmBgYAoKYGdncGxvdDJgIGNvbG9yZWQgc2NhdHRlciBwbG90IGV4YW1wbGU6CgpgYGAge3J9CiAgZ2dwbG90KHN1YnNldChob3VzaW5nLCBTdGF0ZSAlaW4lIGMoIk1BIiwgIlRYIikpLAogICAgICAgICBhZXMoeD1EYXRlLAogICAgICAgICAgICAgeT1Ib21lLlZhbHVlLAogICAgICAgICAgICAgY29sb3I9U3RhdGUpKSsKICAgIGdlb21fcG9pbnQoKQpgYGAKCmBnZ3Bsb3QyYCB3aW5zIQoKR2VvbWV0cmljIE9iamVjdHMgQW5kIEFlc3RoZXRpY3MKPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCkFlc3RoZXRpYyBNYXBwaW5nCi0tLS0tLS0tLS0tLS0tLS0tCgpJbiBnZ3Bsb3QgbGFuZCAqYWVzdGhldGljKiBtZWFucyAic29tZXRoaW5nIHlvdSBjYW4gc2VlIi4gRXhhbXBsZXMgaW5jbHVkZToKCi0gICBwb3NpdGlvbiAoaS5lLiwgb24gdGhlIHggYW5kIHkgYXhlcykKLSAgIGNvbG9yICgib3V0c2lkZSIgY29sb3IpCi0gICBmaWxsICgiaW5zaWRlIiBjb2xvcikKLSAgIHNoYXBlIChvZiBwb2ludHMpCi0gICBsaW5ldHlwZQotICAgc2l6ZQoKRWFjaCB0eXBlIG9mIGdlb20gYWNjZXB0cyBvbmx5IGEgc3Vic2V0IG9mIGFsbCBhZXN0aGV0aWNzLS1yZWZlciB0byB0aGUgZ2VvbSBoZWxwIHBhZ2VzIHRvIHNlZSB3aGF0IG1hcHBpbmdzIGVhY2ggZ2VvbSBhY2NlcHRzLiBBZXN0aGV0aWMgbWFwcGluZ3MgYXJlIHNldCB3aXRoIHRoZSBgYWVzKClgIGZ1bmN0aW9uLgoKR2VvbWV0aWMgT2JqZWN0cyAoYGdlb21gKQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpHZW9tZXRyaWMgb2JqZWN0cyBhcmUgdGhlIGFjdHVhbCBtYXJrcyB3ZSBwdXQgb24gYSBwbG90LiBFeGFtcGxlcyBpbmNsdWRlOgoKLSAgIHBvaW50cyAoYGdlb21fcG9pbnRgLCBmb3Igc2NhdHRlciBwbG90cywgZG90IHBsb3RzLCBldGMpCi0gICBsaW5lcyAoYGdlb21fbGluZWAsIGZvciB0aW1lIHNlcmllcywgdHJlbmQgbGluZXMsIGV0YykKLSAgIGJveHBsb3QgKGBnZW9tX2JveHBsb3RgLCBmb3IsIHdlbGwsIGJveHBsb3RzISkKCkEgcGxvdCBtdXN0IGhhdmUgYXQgbGVhc3Qgb25lIGdlb207IHRoZXJlIGlzIG5vIHVwcGVyIGxpbWl0LiBZb3UgY2FuIGFkZCBhIGdlb20gdG8gYSBwbG90IHVzaW5nIHRoZSBgK2Agb3BlcmF0b3IKCllvdSBjYW4gZ2V0IGEgbGlzdCBvZiBhdmFpbGFibGUgZ2VvbWV0cmljIG9iamVjdHMgdXNpbmcgdGhlIGNvZGUgYmVsb3c6CgpgYGAge3J9CiAgaGVscC5zZWFyY2goImdlb21fIiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikKYGBgCgpvciBzaW1wbHkgdHlwZSBgZ2VvbV88dGFiPmAgaW4gYW55IGdvb2QgUiBJREUgKHN1Y2ggYXMgUnN0dWRpbyBvciBFU1MpIHRvIHNlZSBhIGxpc3Qgb2YgZnVuY3Rpb25zIHN0YXJ0aW5nIHdpdGggYGdlb21fYC4KClBvaW50cyAoU2NhdHRlcnBsb3QpCi0tLS0tLS0tLS0tLS0tLS0tLS0tCgpOb3cgdGhhdCB3ZSBrbm93IGFib3V0IGdlb21ldHJpYyBvYmplY3RzIGFuZCBhZXN0aGV0aWMgbWFwcGluZywgd2UgY2FuIG1ha2UgYSBnZ3Bsb3QuIGBnZW9tX3BvaW50YCByZXF1aXJlcyBtYXBwaW5ncyBmb3IgeCBhbmQgeSwgYWxsIG90aGVycyBhcmUgb3B0aW9uYWwuCgpgYGAge3J9CiAgaHAyMDAxUTEgPC0gc3Vic2V0KGhvdXNpbmcsIERhdGUgPT0gMjAwMTEpIAogIGdncGxvdChocDIwMDFRMSwKICAgICAgICAgYWVzKHkgPSBTdHJ1Y3R1cmUuQ29zdCwgeCA9IExhbmQuVmFsdWUpKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgpMaW5lcyAoUHJlZGljdGlvbiBMaW5lKQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQSBwbG90IGNvbnN0cnVjdGVkIHdpdGggYGdncGxvdGAgY2FuIGhhdmUgbW9yZSB0aGFuIG9uZSBnZW9tLiBJbiB0aGF0IGNhc2UgdGhlIG1hcHBpbmdzIGVzdGFibGlzaGVkIGluIHRoZSBgZ2dwbG90KClgIGNhbGwgYXJlIHBsb3QgZGVmYXVsdHMgdGhhdCBjYW4gYmUgYWRkZWQgdG8gb3Igb3ZlcnJpZGRlbi4gT3VyIHBsb3QgY291bGQgdXNlIGEgcmVncmVzc2lvbiBsaW5lOgoKYGBgIHtyfQogIGhwMjAwMVExJHByZWQuU0MgPC0gcHJlZGljdChsbShTdHJ1Y3R1cmUuQ29zdCB+IExhbmQuVmFsdWUsIGRhdGEgPSBocDIwMDFRMSkpCgogIHAxIDwtIGdncGxvdChocDIwMDFRMSwgYWVzKHggPSBMYW5kLlZhbHVlLCB5ID0gU3RydWN0dXJlLkNvc3QpKQoKICBwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSG9tZS5WYWx1ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWQuU0MpKQpgYGAKClNtb290aGVycwotLS0tLS0tLS0KCk5vdCBhbGwgZ2VvbWV0cmljIG9iamVjdHMgYXJlIHNpbXBsZSBzaGFwZXMtLXRoZSBzbW9vdGggZ2VvbSBpbmNsdWRlcyBhIGxpbmUgYW5kIGEgcmliYm9uLgoKYGBgIHtyfQogIHAxICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSG9tZS5WYWx1ZSkpICsKICAgIGdlb21fc21vb3RoKCkKYGBgCgpUZXh0IChMYWJlbCBQb2ludHMpCi0tLS0tLS0tLS0tLS0tLS0tLS0KCkVhY2ggYGdlb21gIGFjY2VwdHMgYSBwYXJ0aWN1YWxhciBzZXQgb2YgbWFwcGluZ3MtLWZvciBleGFtcGxlIGBnZW9tX3RleHQoKWAgYWNjZXB0cyBhIGBsYWJlbHNgIG1hcHBpbmcuCgpgYGAge3J9CiAgcDEgKyAKICAgIGdlb21fdGV4dChhZXMobGFiZWw9U3RhdGUpLCBzaXplID0gMykKYGBgCgpgYGAge3J9CiAgIyMgaW5zdGFsbC5wYWNrYWdlcygiZ2dyZXBlbCIpIAogIGxpYnJhcnkoImdncmVwZWwiKQogIHAxICsgCiAgICBnZW9tX3BvaW50KCkgKyAKICAgIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9U3RhdGUpLCBzaXplID0gMykKYGBgCgpBZXN0aGV0aWMgTWFwcGluZyBWUyBBc3NpZ25tZW50Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCk5vdGUgdGhhdCB2YXJpYWJsZXMgYXJlIG1hcHBlZCB0byBhZXN0aGV0aWNzIHdpdGggdGhlIGBhZXMoKWAgZnVuY3Rpb24sIHdoaWxlIGZpeGVkIGFlc3RoZXRpY3MgYXJlIHNldCBvdXRzaWRlIHRoZSBgYWVzKClgIGNhbGwuIFRoaXMgc29tZXRpbWVzIGxlYWRzIHRvIGNvbmZ1c2lvbiwgYXMgaW4gdGhpcyBleGFtcGxlOgoKYGBgIHtyfQogIHAxICsKICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSAyKSwjIGluY29ycmVjdCEgMiBpcyBub3QgYSB2YXJpYWJsZQogICAgICAgICAgICAgICBjb2xvcj0icmVkIikgIyB0aGlzIGlzIGZpbmUgLS0gYWxsIHBvaW50cyByZWQKYGBgCgpNYXBwaW5nIFZhcmlhYmxlcyBUbyBPdGhlciBBZXN0aGV0aWNzCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCk90aGVyIGFlc3RoZXRpY3MgYXJlIG1hcHBlZCBpbiB0aGUgc2FtZSB3YXkgYXMgeCBhbmQgeSBpbiB0aGUgcHJldmlvdXMgZXhhbXBsZS4KCmBgYCB7cn0KICBwMSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1Ib21lLlZhbHVlLCBzaGFwZSA9IHJlZ2lvbikpCmBgYAoKRXhlcmNpc2UgSQotLS0tLS0tLS0tCgpUaGUgZGF0YSBmb3IgdGhlIGV4ZXJjaXNlcyBpcyBhdmFpbGFibGUgaW4gdGhlIGBkYXRhU2V0cy9FY29ub21pc3REYXRhLmNzdmAgZmlsZS4gUmVhZCBpdCBpbiB3aXRoCgpgYGAge3J9CiAgZGF0IDwtIHJlYWQuY3N2KCJkYXRhU2V0cy9FY29ub21pc3REYXRhLmNzdiIpCmBgYAoKT3JpZ2luYWwgc291cmNlcyBmb3IgdGhlc2UgZGF0YSBhcmUgPGh0dHA6Ly93d3cudHJhbnNwYXJlbmN5Lm9yZy9jb250ZW50L2Rvd25sb2FkLzY0NDc2LzEwMzE0Mjg+IDxodHRwOi8vaGRyc3RhdHMudW5kcC5vcmcvZW4vaW5kaWNhdG9ycy9kaXNwbGF5X2NmX3hsc19pbmRpY2F0b3IuY2ZtP2luZGljYXRvcl9pZD0xMDMxMDYmbGFuZz1lbj4KClRoZXNlIGRhdGEgY29uc2lzdCBvZiAqSHVtYW4gRGV2ZWxvcG1lbnQgSW5kZXgqIGFuZCAqQ29ycnVwdGlvbiBQZXJjZXB0aW9uIEluZGV4KiBzY29yZXMgZm9yIHNldmVyYWwgY291bnRyaWVzLgoKMS4gIENyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB3aXRoIENQSSBvbiB0aGUgeCBheGlzIGFuZCBIREkgb24gdGhlIHkgYXhpcy4KMi4gIENvbG9yIHRoZSBwb2ludHMgaW4gdGhlIHByZXZpb3VzIHBsb3QgYmx1ZS4KMy4gIENvbG9yIHRoZSBwb2ludHMgaW4gdGhlIHByZXZpb3VzIHBsb3QgYWNjb3JkaW5nIHRvICpSZWdpb24qLgo0LiAgQ3JlYXRlIGJveHBsb3RzIG9mIENQSSBieSBSZWdpb24KNS4gIE92ZXJsYXkgcG9pbnRzIG9uIHRvcCBvZiB0aGUgYm94IHBsb3RzCgpFeGVyY2lzZSBJIHByb3RvdHlwZTxzcGFuIGNsYXNzPSJ0YWciIGRhdGEtdGFnLW5hbWU9InByb3RvdHlwZSI+PC9zcGFuPgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKMS4gIFxbQDFcXSBDcmVhdGUgYSBzY2F0dGVyIHBsb3Qgd2l0aCBDUEkgb24gdGhlIHggYXhpcyBhbmQgSERJIG9uIHRoZSB5IGF4aXMuCgpgYGAge3J9CiAgZ2dwbG90KGRhdCwgYWVzKHggPSBDUEksIHkgPSBIREkpKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgoxLiAgXFtAMlxdIENvbG9yIHRoZSBwb2ludHMgaW4gdGhlIHByZXZpb3VzIHBsb3QgYmx1ZS4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSkpICsKICAgIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIpCmBgYAoKMS4gIFxbQDNcXSBDb2xvciB0aGUgcG9pbnRzIGluIHRoZSBwcmV2aW91cyBwbG90IGFjY29yZGluZyB0byAqUmVnaW9uKi4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSkpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gUmVnaW9uKSkKYGBgCgoxLiAgXFtANFxdIENyZWF0ZSBib3hwbG90cyBvZiBDUEkgYnkgUmVnaW9uCgpgYGAge3J9CiAgZ2dwbG90KGRhdCwgYWVzKHggPSBSZWdpb24sIHkgPSBDUEkpKSArCiAgICBnZW9tX2JveHBsb3QoKQpgYGAKCjEuICBcW0A1XF0gT3ZlcmxheSBwb2ludHMgb24gdG9wIG9mIHRoZSBib3ggcGxvdHMKCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IFJlZ2lvbiwgeSA9IENQSSkpICsKICAgIGdlb21fYm94cGxvdCgpICsKICAgIGdlb21fcG9pbnQoKSAKYGBgCgpTdGF0aXN0aWNhbCBUcmFuc2Zvcm1hdGlvbnMKPT09PT09PT09PT09PT09PT09PT09PT09PT09CgpTdGF0aXN0aWNhbCBUcmFuc2Zvcm1hdGlvbnMKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpTb21lIHBsb3QgdHlwZXMgKHN1Y2ggYXMgc2NhdHRlcnBsb3RzKSBkbyBub3QgcmVxdWlyZSB0cmFuc2Zvcm1hdGlvbnMtLWVhY2ggcG9pbnQgaXMgcGxvdHRlZCBhdCB4IGFuZCB5IGNvb3JkaW5hdGVzIGVxdWFsIHRvIHRoZSBvcmlnaW5hbCB2YWx1ZS4gT3RoZXIgcGxvdHMsIHN1Y2ggYXMgYm94cGxvdHMsIGhpc3RvZ3JhbXMsIHByZWRpY3Rpb24gbGluZXMgZXRjLiByZXF1aXJlIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uczoKCi0gICBmb3IgYSBib3hwbG90IHRoZSB5IHZhbHVlcyBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIHRoZSBtZWRpYW4gYW5kIDEuNShJUVIpCi0gICBmb3IgYSBzbW9vdGhlciBzbW90aGVyIHRoZSB5IHZhbHVlcyBtdXN0IGJlIHRyYW5zZm9ybWVkIGludG8gcHJlZGljdGVkIHZhbHVlcwoKRWFjaCBgZ2VvbWAgaGFzIGEgZGVmYXVsdCBzdGF0aXN0aWMsIGJ1dCB0aGVzZSBjYW4gYmUgY2hhbmdlZC4gRm9yIGV4YW1wbGUsIHRoZSBkZWZhdWx0IHN0YXRpc3RpYyBmb3IgYGdlb21fYmFyYCBpcyBgc3RhdF9jb3VudGA6CgpgYGAge3J9CiAgYXJncyhnZW9tX2hpc3RvZ3JhbSkKICBhcmdzKHN0YXRfYmluKQpgYGAKClNldHRpbmcgU3RhdGlzdGljYWwgVHJhbnNmb3JtYXRpb24gQXJndW1lbnRzCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpBcmd1bWVudHMgdG8gYHN0YXRfYCBmdW5jdGlvbnMgY2FuIGJlIHBhc3NlZCB0aHJvdWdoIGBnZW9tX2AgZnVuY3Rpb25zLiBUaGlzIGNhbiBiZSBzbGlnaHRseSBhbm5veWluZyBiZWNhdXNlIGluIG9yZGVyIHRvIGNoYW5nZSBpdCB5b3UgaGF2ZSB0byBmaXJzdCBkZXRlcm1pbmUgd2hpY2ggc3RhdCB0aGUgZ2VvbSB1c2VzLCB0aGVuIGRldGVybWluZSB0aGUgYXJndW1lbnRzIHRvIHRoYXQgc3RhdC4KCkZvciBleGFtcGxlLCBoZXJlIGlzIHRoZSBkZWZhdWx0IGhpc3RvZ3JhbSBvZiBIb21lLlZhbHVlOgoKYGBgIHtyfQogIHAyIDwtIGdncGxvdChob3VzaW5nLCBhZXMoeCA9IEhvbWUuVmFsdWUpKQogIHAyICsgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKClRoZSBiaW53aWR0aCBsb29rcyByZWFzb25hYmxlIGJ5IGRlZmF1bHQsIGJ1dCB3ZSBjYW4gY2hhbmdlIGl0IGJ5IHBhc3NpbmcgdGhlIGBiaW53aWR0aGAgYXJndW1lbnQgdG8gdGhlIGBzdGF0X2JpbmAgZnVuY3Rpb246CgpgYGAge3J9CiAgcDIgKyBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImJpbiIsIGJpbndpZHRoPTQwMDApCmBgYAoKQ2hhbmdpbmcgVGhlIFN0YXRpc3RpY2FsIFRyYW5zZm9ybWF0aW9uCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKU29tZXRpbWVzIHRoZSBkZWZhdWx0IHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIGlzIG5vdCB3aGF0IHlvdSBuZWVkLiBUaGlzIGlzIG9mdGVuIHRoZSBjYXNlIHdpdGggcHJlLXN1bW1hcml6ZWQgZGF0YToKCmBgYCB7cn0KICBob3VzaW5nLnN1bSA8LSBhZ2dyZWdhdGUoaG91c2luZ1siSG9tZS5WYWx1ZSJdLCBob3VzaW5nWyJTdGF0ZSJdLCBGVU49bWVhbikKICByYmluZChoZWFkKGhvdXNpbmcuc3VtKSwgdGFpbChob3VzaW5nLnN1bSkpCmBgYAoKYGBgIHtyfQojIHRyeSgKIyAgIHsKIyAgICAgIGdncGxvdChob3VzaW5nLnN1bSwgYWVzKHg9U3RhdGUsIHk9SG9tZS5WYWx1ZSkpICsgCiMgICAgIGdlb21fYmFyKCkKIyAgIH0KIyAKIyApCmBgYAoKYGBgIGV4YW1wbGUKZ2dwbG90KGhvdXNpbmcuc3VtLCBhZXMoeD1TdGF0ZSwgeT1Ib21lLlZhbHVlKSkgKyAKICBnZW9tX2JhcigpCkVycm9yOiBzdGF0X2NvdW50KCkgbXVzdCBub3QgYmUgdXNlZCB3aXRoIGEgeSBhZXN0aGV0aWMuCmBgYAoKV2hhdCBpcyB0aGUgcHJvYmxlbSB3aXRoIHRoZSBwcmV2aW91cyBwbG90PyBCYXNpY2FsbHkgd2UgdGFrZSBiaW5uZWQgYW5kIHN1bW1hcml6ZWQgZGF0YSBhbmQgYXNrIGdncGxvdCB0byBiaW4gYW5kIHN1bW1hcml6ZSBpdCBhZ2FpbiAocmVtZW1iZXIsIGBnZW9tX2JhcmAgZGVmYXVsdHMgdG8gYHN0YXQgYCBzdGF0PHN1Yj5jb3VudDwvc3ViPj0pOyBvYnZpb3VzbHkgdGhpcyB3aWxsIG5vdCB3b3JrLiBXZSBjYW4gZml4IGl0IGJ5IHRlbGxpbmcgYGdlb21fYmFyYCB0byB1c2UgYSBkaWZmZXJlbnQgc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb24gZnVuY3Rpb246CgpgYGAge3J9CiAgZ2dwbG90KGhvdXNpbmcuc3VtLCBhZXMoeD1TdGF0ZSwgeT1Ib21lLlZhbHVlKSkgKyAKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgpFeGVyY2lzZSBJSQotLS0tLS0tLS0tLQoKMS4gIFJlLWNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB3aXRoIENQSSBvbiB0aGUgeCBheGlzIGFuZCBIREkgb24gdGhlIHkgYXhpcyAoYXMgeW91IGRpZCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UpLgoyLiAgT3ZlcmxheSBhIHNtb290aGluZyBsaW5lIG9uIHRvcCBvZiB0aGUgc2NhdHRlciBwbG90IHVzaW5nIHRoZSAqbG0qIG1ldGhvZC4gSGludDogc2VlIGA/c3RhdF9zbW9vdGhgLgozLiAgT3ZlcmxheSBhIHNtb290aGluZyBsaW5lIG9uIHRvcCBvZiB0aGUgc2NhdHRlciBwbG90IHVzaW5nIHRoZSBkZWZhdWx0IG1ldGhvZC4KNC4gIEJPTlVTIChvcHRpb25hbCk6IE92ZXJsYXkgYSBzbW9vdGhpbmcgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgZGVmYXVsdCAqbG9lc3MqIG1ldGhvZCwgYnV0IG1ha2UgaXQgbGVzcyBzbW9vdGguIEhpbnQ6IHNlZSBgP2xvZXNzYC4KCkV4ZXJjaXNlIElJIHByb3RvdHlwZTxzcGFuIGNsYXNzPSJ0YWciIGRhdGEtdGFnLW5hbWU9InByb3RvdHlwZSI+PC9zcGFuPgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjEuICBcW0AxXF0gUmUtY3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggQ1BJIG9uIHRoZSB4IGF4aXMgYW5kIEhESSBvbiB0aGUgeSBheGlzIChhcyB5b3UgZGlkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSkuCgpgYGAge3J9CiAgZ2dwbG90KGRhdCwgYWVzKHggPSBDUEksIHkgPSBIREkpKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgoxLiAgXFtAMlxdIE92ZXJsYXkgYSBzbW9vdGhpbmcgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgKmxtKiBtZXRob2QuIEhpbnQ6IHNlZSBgP3N0YXRfc21vb3RoYC4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQpgYGAKCjEuICBcW0AzXF0gT3ZlcmxheSBhIHNtb290aGluZyBsaW5lIG9uIHRvcCBvZiB0aGUgc2NhdHRlciBwbG90IHVzaW5nIHRoZSBkZWZhdWx0IG1ldGhvZC4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKMS4gIFxbQDRcXSBCT05VUyAob3B0aW9uYWwpOiBPdmVybGF5IGEgc21vb3RoaW5nIGxpbmUgb24gdG9wIG9mIHRoZSBzY2F0dGVyIHBsb3QgdXNpbmcgdGhlIGRlZmF1bHQgKmxvZXNzKiBtZXRob2QsIGJ1dCBtYWtlIGl0IGxlc3Mgc21vb3RoLiBIaW50OiBzZWUgYD9sb2Vzc2AuCgpgYGAge3J9CiAgZ2dwbG90KGRhdCwgYWVzKHggPSBDUEksIHkgPSBIREkpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgoc3BhbiA9IC40KQpgYGAKClNjYWxlcwo9PT09PT0KClNjYWxlczogQ29udHJvbGxpbmcgQWVzdGhldGljIE1hcHBpbmcKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQWVzdGhldGljIG1hcHBpbmcgKGkuZS4sIHdpdGggYGFlcygpYCkgb25seSBzYXlzIHRoYXQgYSB2YXJpYWJsZSBzaG91bGQgYmUgbWFwcGVkIHRvIGFuIGFlc3RoZXRpYy4gSXQgZG9lc24ndCBzYXkgKmhvdyogdGhhdCBzaG91bGQgaGFwcHkuIEZvciBleGFtcGxlLCB3aGVuIG1hcHBpbmcgYSB2YXJpYWJsZSB0byAqc2hhcGUqIHdpdGggYGFlcyhzaGFwZSBgIHgpPSB5b3UgZG9uJ3Qgc2F5ICp3aGF0KiBzaGFwZXMgc2hvdWxkIGJlIHVzZWQuIFNpbWlsYXJseSwgYGFlcyhjb2xvciBgIHopPSBkb2Vzbid0IHNheSAqd2hhdCogY29sb3JzIHNob3VsZCBiZSB1c2VkLiBEZXNjcmliaW5nIHdoYXQgY29sb3JzL3NoYXBlcy9zaXplcyBldGMuIHRvIHVzZSBpcyBkb25lIGJ5IG1vZGlmeWluZyB0aGUgY29ycmVzcG9uZGluZyAqc2NhbGUqLiBJbiBgZ2dwbG90MmAgc2NhbGVzIGluY2x1ZGUKCi0gICBwb3NpdGlvbgotICAgY29sb3IgYW5kIGZpbGwKLSAgIHNpemUKLSAgIHNoYXBlCi0gICBsaW5lIHR5cGUKClNjYWxlcyBhcmUgbW9kaWZpZWQgd2l0aCBhIHNlcmllcyBvZiBmdW5jdGlvbnMgdXNpbmcgYSBgc2NhbGVfPGFlc3RoZXRpYz5fPHR5cGU+YCBuYW1pbmcgc2NoZW1lLiBUcnkgdHlwaW5nIGBzY2FsZV88dGFiPmAgdG8gc2VlIGEgbGlzdCBvZiBzY2FsZSBtb2RpZmljYXRpb24gZnVuY3Rpb25zLgoKQ29tbW9uIFNjYWxlIEFyZ3VtZW50cwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpUaGUgZm9sbG93aW5nIGFyZ3VtZW50cyBhcmUgY29tbW9uIHRvIG1vc3Qgc2NhbGVzIGluIGdncGxvdDI6CgpuYW1lICAKdGhlIGZpcnN0IGFyZ3VtZW50IGdpdmVzIHRoZSBheGlzIG9yIGxlZ2VuZCB0aXRsZQoKbGltaXRzICAKdGhlIG1pbmltdW0gYW5kIG1heGltdW0gb2YgdGhlIHNjYWxlCgpicmVha3MgIAp0aGUgcG9pbnRzIGFsb25nIHRoZSBzY2FsZSB3aGVyZSBsYWJlbHMgc2hvdWxkIGFwcGVhcgoKbGFiZWxzICAKdGhlIGxhYmVscyB0aGF0IGFwcGVhciBhdCBlYWNoIGJyZWFrCgpTcGVjaWZpYyBzY2FsZSBmdW5jdGlvbnMgbWF5IGhhdmUgYWRkaXRpb25hbCBhcmd1bWVudHM7IGZvciBleGFtcGxlLCB0aGUgYHNjYWxlX2NvbG9yX2NvbnRpbnVvdXNgIGZ1bmN0aW9uIGhhcyBhcmd1bWVudHMgYGxvd2AgYW5kIGBoaWdoYCBmb3Igc2V0dGluZyB0aGUgY29sb3JzIGF0IHRoZSBsb3cgYW5kIGhpZ2ggZW5kIG9mIHRoZSBzY2FsZS4KClNjYWxlIE1vZGlmaWNhdGlvbiBFeGFtcGxlcwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClN0YXJ0IGJ5IGNvbnN0cnVjdGluZyBhIGRvdHBsb3Qgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGhvbWUgdmFsdWVzIGJ5IERhdGUgYW5kIFN0YXRlLgoKYGBgIHtyfQogIHAzIDwtIGdncGxvdChob3VzaW5nLAogICAgICAgICAgICAgICBhZXMoeCA9IFN0YXRlLAogICAgICAgICAgICAgICAgICAgeSA9IEhvbWUuUHJpY2UuSW5kZXgpKSArIAogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLAogICAgICAgICAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplID0gNikpCiAgKHA0IDwtIHAzICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBEYXRlKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4yNSwgaGVpZ2h0ID0gMCkpKQpgYGAKCk5vdyBtb2RpZnkgdGhlIGJyZWFrcyBhbmQgbGFiZWxzIGZvciB0aGUgeCBheGlzIGFuZCBjb2xvciBzY2FsZXMKCmBgYCB7cn0KICBwNCArIHNjYWxlX3hfZGlzY3JldGUobmFtZT0iU3RhdGUgQWJicmV2aWF0aW9uIikgKwogICAgc2NhbGVfY29sb3JfY29udGludW91cyhuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDE5NzUxLCAxOTk0MSwgMjAxMzEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKDE5NzEsIDE5OTQsIDIwMTMpKQpgYGAKCk5leHQgY2hhbmdlIHRoZSBsb3cgYW5kIGhpZ2ggdmFsdWVzIHRvIGJsdWUgYW5kIHJlZDoKCmBgYCB7cn0KICBwNCArCiAgICBzY2FsZV94X2Rpc2NyZXRlKG5hbWU9IlN0YXRlIEFiYnJldmlhdGlvbiIpICsKICAgIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygxOTc1MSwgMTk5NDEsIDIwMTMxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygxOTcxLCAxOTk0LCAyMDEzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpCmBgYAoKYGBgIHtyfQpsaWJyYXJ5KHNjYWxlcykgIyBQYXJhIHVzYXIgbGEgZnVuY2nDs24gbXV0ZWQKICBwNCArCiAgICBzY2FsZV9jb2xvcl9jb250aW51b3VzKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMTk3NTEsIDE5OTQxLCAyMDEzMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoMTk3MSwgMTk5NCwgMjAxMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvdyA9IG11dGVkKCJibHVlIiksIGhpZ2ggPSBtdXRlZCgicmVkIikpCmBgYAoKVXNpbmcgZGlmZmVyZW50IGNvbG9yIHNjYWxlcwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpnZ3Bsb3QyIGhhcyBhIHdpZGUgdmFyaWV0eSBvZiBjb2xvciBzY2FsZXM7IGhlcmUgaXMgYW4gZXhhbXBsZSB1c2luZyBgc2NhbGVfY29sb3JfZ3JhZGllbnQyYCB0byBpbnRlcnBvbGF0ZSBiZXR3ZWVuIHRocmVlIGRpZmZlcmVudCBjb2xvcnMuCgpgYGAge3J9CmxpYnJhcnkoc2NhbGVzKQogIHA0ICsKICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50MihuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMTk3NTEsIDE5OTQxLCAyMDEzMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygxOTcxLCAxOTk0LCAyMDEzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSBtdXRlZCgiYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSBtdXRlZCgicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gImdyYXk2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAxOTk0MSkKYGBgCgpBdmFpbGFibGUgU2NhbGVzCi0tLS0tLS0tLS0tLS0tLS0KCi0gICBQYXJ0aWFsIGNvbWJpbmF0aW9uIG1hdHJpeCBvZiBhdmFpbGFibGUgc2NhbGVzCgp8ICoqU2NhbGUqKiAgICAgICAgICAgICAgICAgIHwgKipUeXBlcyoqICB8ICoqRXhhbXBsZXMqKiAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Cnwgc2NhbGU8c3ViPmNvbG9yPC9zdWI+XF8gICAgfCBpZGVudGl0eSAgIHwgc2NhbGU8c3ViPmZpbGxjb250aW51b3VzPC9zdWI+ICAgfAp8IHNjYWxlPHN1Yj5maWxsPC9zdWI+XF8gICAgIHwgbWFudWFsICAgICB8IHNjYWxlPHN1Yj5jb2xvcmRpc2NyZXRlPC9zdWI+ICAgIHwKfCBzY2FsZTxzdWI+c2l6ZTwvc3ViPlxfICAgICB8IGNvbnRpbnVvdXMgfCBzY2FsZTxzdWI+c2l6ZW1hbnVhbDwvc3ViPiAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBkaXNjcmV0ZSAgIHwgc2NhbGU8c3ViPnNpemVkaXNjcmV0ZTwvc3ViPiAgICAgfAp8IHNjYWxlPHN1Yj5zaGFwZTwvc3ViPlxfICAgIHwgZGlzY3JldGUgICB8IHNjYWxlPHN1Yj5zaGFwZWRpc2NyZXRlPC9zdWI+ICAgIHwKfCBzY2FsZTxzdWI+bGluZXR5cGU8L3N1Yj5cXyB8IGlkZW50aXR5ICAgfCBzY2FsZTxzdWI+c2hhcGVtYW51YWw8L3N1Yj4gICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBtYW51YWwgICAgIHwgc2NhbGU8c3ViPmxpbmV0eXBlZGlzY3JldGU8L3N1Yj4gfAp8IHNjYWxlPHN1Yj54PC9zdWI+XF8gICAgICAgIHwgY29udGludW91cyB8IHNjYWxlPHN1Yj54Y29udGludW91czwvc3ViPiAgICAgIHwKfCBzY2FsZTxzdWI+eTwvc3ViPlxfICAgICAgICB8IGRpc2NyZXRlICAgfCBzY2FsZTxzdWI+eWRpc2NyZXRlPC9zdWI+ICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCByZXZlcnNlICAgIHwgc2NhbGU8c3ViPnhsb2c8L3N1Yj4gICAgICAgICAgICAgfAp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgbG9nICAgICAgICB8IHNjYWxlPHN1Yj55cmV2ZXJzZTwvc3ViPiAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGRhdGUgICAgICAgfCBzY2FsZTxzdWI+eGRhdGU8L3N1Yj4gICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBkYXRldGltZSAgIHwgc2NhbGU8c3ViPnlkYXRldGltZTwvc3ViPiAgICAgICAgfAoKTm90ZSB0aGF0IGluIFJTdHVkaW8geW91IGNhbiB0eXBlIGBzY2FsZV9gIGZvbGxvd2VkIGJ5IFRBQiB0byBnZXQgdGhlIHdob2xlIGxpc3Qgb2YgYXZhaWxhYmxlIHNjYWxlcy4KCkV4ZXJjaXNlIElJSQotLS0tLS0tLS0tLS0KCjEuICBDcmVhdGUgYSBzY2F0dGVyIHBsb3Qgd2l0aCBDUEkgb24gdGhlIHggYXhpcyBhbmQgSERJIG9uIHRoZSB5IGF4aXMuIENvbG9yIHRoZSBwb2ludHMgdG8gaW5kaWNhdGUgcmVnaW9uLgoyLiAgTW9kaWZ5IHRoZSB4LCB5LCBhbmQgY29sb3Igc2NhbGVzIHNvIHRoYXQgdGhleSBoYXZlIG1vcmUgZWFzaWx5LXVuZGVyc3Rvb2QgbmFtZXMgKGUuZy4sIHNwZWxsIG91dCAiSHVtYW4gZGV2ZWxvcG1lbnQgSW5kZXgiIGluc3RlYWQgb2YgIkhESSIpLgozLiAgTW9kaWZ5IHRoZSBjb2xvciBzY2FsZSB0byB1c2Ugc3BlY2lmaWMgdmFsdWVzIG9mIHlvdXIgY2hvb3NpbmcuIEhpbnQ6IHNlZSBgP3NjYWxlX2NvbG9yX21hbnVhbGAuCgpFeGVyY2lzZSBJSUkgcHJvdG90eXBlPHNwYW4gY2xhc3M9InRhZyIgZGF0YS10YWctbmFtZT0icHJvdG90eXBlIj48L3NwYW4+Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjEuICBcW0AxXF0gQ3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggQ1BJIG9uIHRoZSB4IGF4aXMgYW5kIEhESSBvbiB0aGUgeSBheGlzLiBDb2xvciB0aGUgcG9pbnRzIHRvIGluZGljYXRlIHJlZ2lvbi4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSwgY29sb3IgPSAiUmVnaW9uIikpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCjEuICBcW0AyXF0gTW9kaWZ5IHRoZSB4LCB5LCBhbmQgY29sb3Igc2NhbGVzIHNvIHRoYXQgdGhleSBoYXZlIG1vcmUgZWFzaWx5LXVuZGVyc3Rvb2QgbmFtZXMgKGUuZy4sIHNwZWxsIG91dCAiSHVtYW4gZGV2ZWxvcG1lbnQgSW5kZXgiIGluc3RlYWQgb2YgIkhESSIpLgoKYGBgIHtyfQogIGdncGxvdChkYXQsIGFlcyh4ID0gQ1BJLCB5ID0gSERJLCBjb2xvciA9ICJSZWdpb24iKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiQ29ycnVwdGlvbiBQZXJjZXB0aW9uIEluZGV4IikgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkh1bWFuIERldmVsb3BtZW50IEluZGV4IikgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUmVnaW9uIG9mIHRoZSB3b3JsZCIpCmBgYAoKMS4gIFxbQDNcXSBNb2RpZnkgdGhlIGNvbG9yIHNjYWxlIHRvIHVzZSBzcGVjaWZpYyB2YWx1ZXMgb2YgeW91ciBjaG9vc2luZy4gSGludDogc2VlIGA/c2NhbGVfY29sb3JfbWFudWFsYC4KCmBgYCB7cn0KICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSwgY29sb3IgPSAiUmVnaW9uIikpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIkNvcnJ1cHRpb24gUGVyY2VwdGlvbiBJbmRleCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJIdW1hbiBEZXZlbG9wbWVudCBJbmRleCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIlJlZ2lvbiBvZiB0aGUgd29ybGQiLAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiMyNDU3NkQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMwOTlERDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMyOEFBREMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMyNDhFODQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGMjU4M0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiM5NjUwM0YiKSkKYGBgCgpGYWNldGluZwo9PT09PT09PQoKRmFjZXRpbmcKLS0tLS0tLS0KCi0gICBGYWNldGluZyBpcyBgZ2dwbG90MmAgcGFybGFuY2UgZm9yICoqc21hbGwgbXVsdGlwbGVzKioKLSAgIFRoZSBpZGVhIGlzIHRvIGNyZWF0ZSBzZXBhcmF0ZSBncmFwaHMgZm9yIHN1YnNldHMgb2YgZGF0YQotICAgYGdncGxvdDJgIG9mZmVycyB0d28gZnVuY3Rpb25zIGZvciBjcmVhdGluZyBzbWFsbCBtdWx0aXBsZXM6CiAgICAxLiAgYGZhY2V0X3dyYXAoKWA6IGRlZmluZSBzdWJzZXRzIGFzIHRoZSBsZXZlbHMgb2YgYSBzaW5nbGUgZ3JvdXBpbmcgdmFyaWFibGUKICAgIDIuICBgZmFjZXRfZ3JpZCgpYDogZGVmaW5lIHN1YnNldHMgYXMgdGhlIGNyb3NzaW5nIG9mIHR3byBncm91cGluZyB2YXJpYWJsZXMKLSAgIEZhY2lsaXRhdGVzIGNvbXBhcmlzb24gYW1vbmcgcGxvdHMsIG5vdCBqdXN0IG9mIGdlb21zIHdpdGhpbiBhIHBsb3QKCldoYXQgaXMgdGhlIHRyZW5kIGluIGhvdXNpbmcgcHJpY2VzIGluIGVhY2ggc3RhdGU/Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotICAgU3RhcnQgYnkgdXNpbmcgYSB0ZWNobmlxdWUgd2UgYWxyZWFkeSBrbm93LS1tYXAgU3RhdGUgdG8gY29sb3I6CgpgYGAge3J9CiAgcDUgPC0gZ2dwbG90KGhvdXNpbmcsIGFlcyh4ID0gRGF0ZSwgeSA9IEhvbWUuVmFsdWUpKQogIHA1ICsgZ2VvbV9saW5lKGFlcyhjb2xvciA9IFN0YXRlKSkgIApgYGAKClRoZXJlIGFyZSB0d28gcHJvYmxlbXMgaGVyZS0tdGhlcmUgYXJlIHRvbyBtYW55IHN0YXRlcyB0byBkaXN0aW5ndWlzaCBlYWNoIG9uZSBieSBjb2xvciwgYW5kIHRoZSBsaW5lcyBvYnNjdXJlIG9uZSBhbm90aGVyLgoKRmFjZXRpbmcgdG8gdGhlIHJlc2N1ZQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpXZSBjYW4gcmVtZWR5IHRoZSBkZWZpY2llbmNpZXMgb2YgdGhlIHByZXZpb3VzIHBsb3QgYnkgZmFjZXRpbmcgYnkgc3RhdGUgcmF0aGVyIHRoYW4gbWFwcGluZyBzdGF0ZSB0byBjb2xvci4KCmBgYCB7cn0KICAocDUgPC0gcDUgKyBnZW9tX2xpbmUoKSArCiAgICAgZmFjZXRfd3JhcCh+U3RhdGUsIG5jb2wgPSAxMCkpCmBgYAoKVGhlcmUgaXMgYWxzbyBhIGBmYWNldF9ncmlkKClgIGZ1bmN0aW9uIGZvciBmYWNldGluZyBpbiB0d28gZGltZW5zaW9ucy4KClRoZW1lcwo9PT09PT0KClRoZW1lcwotLS0tLS0KClRoZSBgZ2dwbG90MmAgdGhlbWUgc3lzdGVtIGhhbmRsZXMgbm9uLWRhdGEgcGxvdCBlbGVtZW50cyBzdWNoIGFzCgotICAgQXhpcyBsYWJlbHMKLSAgIFBsb3QgYmFja2dyb3VuZAotICAgRmFjZXQgbGFiZWwgYmFja3JvdW5kCi0gICBMZWdlbmQgYXBwZWFyYW5jZQoKQnVpbHQtaW4gdGhlbWVzIGluY2x1ZGU6CgotICAgYHRoZW1lX2dyYXkoKWAgKGRlZmF1bHQpCi0gICBgdGhlbWVfYncoKWAKLSAgIGB0aGVtZV9jbGFzc2MoKWAKCmBgYCB7cn0KICBwNSArIHRoZW1lX2xpbmVkcmF3KCkKYGBgCgpgYGAge3J9CiAgcDUgKyB0aGVtZV9saWdodCgpCmBgYAoKT3ZlcnJpZGluZyB0aGVtZSBkZWZhdWx0cwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpTcGVjaWZpYyB0aGVtZSBlbGVtZW50cyBjYW4gYmUgb3ZlcnJpZGRlbiB1c2luZyBgdGhlbWUoKWAuIEZvciBleGFtcGxlOgoKYGBgIHtyfQogIHA1ICsgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAidHVycXVvaXNlIikpCmBgYAoKQWxsIHRoZW1lIG9wdGlvbnMgYXJlIGRvY3VtZW50ZWQgaW4gYD90aGVtZWAuCgpDcmVhdGluZyBhbmQgc2F2aW5nIG5ldyB0aGVtZXMKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpZb3UgY2FuIGNyZWF0ZSBuZXcgdGhlbWVzLCBhcyBpbiB0aGUgZm9sbG93aW5nIGV4YW1wbGU6CgpgYGAge3J9CiAgIyB0aGVtZV9uZXcgPC0gdGhlbWVfYncoKSArCiAgIyAgIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChzaXplID0gMSwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAiYmxhY2siKSwKICAjICAgICAgICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYW1pbHkgPSAiU2VyaWYiLCBjb2xvciA9ICJpdm9yeSIpLAogICMgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAicHVycGxlIiksCiAgIyAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJyZWQiKSwKICAjICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInBpbmsiKSwKICAjICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gbXV0ZWQoIm9yYW5nZSIpKSkKICAjIAogICMgcDUgKyB0aGVtZV9uZXcKYGBgCgpUaGUgXCMxIEZBUQo9PT09PT09PT09PQoKTWFwIEFlc3RoZXRpYyBUbyBEaWZmZXJlbnQgQ29sdW1ucwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpUaGUgbW9zdCBmcmVxdWVudGx5IGFza2VkIHF1ZXN0aW9uIGdvZXMgc29tZXRoaW5nIGxpa2UgdGhpczogKkkgaGF2ZSB0d28gdmFyaWFibGVzIGluIG15IGRhdGEuZnJhbWUsIGFuZCBJJ2QgbGlrZSB0byBwbG90IHRoZW0gYXMgc2VwYXJhdGUgcG9pbnRzLCB3aXRoIGRpZmZlcmVudCBjb2xvciBkZXBlbmRpbmcgb24gd2hpY2ggdmFyaWFibGUgaXQgaXMuIEhvdyBkbyBJIGRvIHRoYXQ/KgoKIyMjIFdyb25nCgpgYGAge3J9CiAgaG91c2luZy5ieXllYXIgPC0gYWdncmVnYXRlKGNiaW5kKEhvbWUuVmFsdWUsIExhbmQuVmFsdWUpIH4gRGF0ZSwgZGF0YSA9IGhvdXNpbmcsIG1lYW4pCiAgZ2dwbG90KGhvdXNpbmcuYnl5ZWFyLAogICAgICAgICBhZXMoeD1EYXRlKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PUhvbWUuVmFsdWUpLCBjb2xvcj0icmVkIikgKwogICAgZ2VvbV9saW5lKGFlcyh5PUxhbmQuVmFsdWUpLCBjb2xvcj0iYmx1ZSIpCgoKICAjCmBgYAoKIyMjIFJpZ2h0CgpgYGAge3J9CiAgbGlicmFyeSh0aWR5cikKICBob21lLmxhbmQuYnl5ZWFyIDwtIGdhdGhlcihob3VzaW5nLmJ5eWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5ID0gInR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhvbWUuVmFsdWUsIExhbmQuVmFsdWUpCiAgZ2dwbG90KGhvbWUubGFuZC5ieXllYXIsCiAgICAgICAgIGFlcyh4PURhdGUsCiAgICAgICAgICAgICB5PXZhbHVlLAogICAgICAgICAgICAgY29sb3I9dHlwZSkpICsKICAgIGdlb21fbGluZSgpCmBgYAoKUHV0dGluZyBJdCBBbGwgVG9nZXRoZXIKPT09PT09PT09PT09PT09PT09PT09PT0KCkNoYWxsZW5nZTogUmVjcmVhdGUgVGhpcyBgRWNvbm9taXN0YCBHcmFwaAotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCltmaWxlOmltYWdlcy9FY29ub21pc3QxLnBkZl0oaW1hZ2VzL0Vjb25vbWlzdDEucGRmKQoKR3JhcGggc291cmNlOiA8aHR0cDovL3d3dy5lY29ub21pc3QuY29tL25vZGUvMjE1NDExNzg+CgpCdWlsZGluZyBvZmYgb2YgdGhlIGdyYXBoaWNzIHlvdSBjcmVhdGVkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZXMsIHB1dCB0aGUgZmluaXNoaW5nIHRvdWNoZXMgdG8gbWFrZSBpdCBhcyBjbG9zZSBhcyBwb3NzaWJsZSB0byB0aGUgb3JpZ2luYWwgZWNvbm9taXN0IGdyYXBoLgoKIVtdKGltYWdlcy9FY29ub21pc3QxLnBuZykKCkNoYWxsZW5nZSBTb2x1dGlvbjxzcGFuIGNsYXNzPSJ0YWciIGRhdGEtdGFnLW5hbWU9InByb3RvdHlwZSI+PC9zcGFuPgo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCkxldHMgc3RhcnQgYnkgY3JlYXRpbmcgdGhlIGJhc2ljIHNjYXR0ZXIgcGxvdCwgdGhlbiB3ZSBjYW4gbWFrZSBhIGxpc3Qgb2YgdGhpbmdzIHRoYXQgbmVlZCB0byBiZSBhZGRlZCBvciBjaGFuZ2VkLiBUaGUgYmFzaWMgcGxvdCBsb29ncyBsaWtlIHRoaXM6CgpgYGAge3J9CiAgZGF0IDwtIHJlYWQuY3N2KCJkYXRhU2V0cy9FY29ub21pc3REYXRhLmNzdiIpCgogIHBjMSA8LSBnZ3Bsb3QoZGF0LCBhZXMoeCA9IENQSSwgeSA9IEhESSwgY29sb3IgPSBSZWdpb24pKQogIHBjMSArIGdlb21fcG9pbnQoKQpgYGAKClRvIGNvbXBsZXRlIHRoaXMgZ3JhcGggd2UgbmVlZCB0bzoKCi0gICBcWyBcXSBhZGQgYSB0cmVuZCBsaW5lCi0gICBcWyBcXSBjaGFuZ2UgdGhlIHBvaW50IHNoYXBlIHRvIG9wZW4gY2lyY2xlCi0gICBcWyBcXSBjaGFuZ2UgdGhlIG9yZGVyIGFuZCBsYWJlbHMgb2YgUmVnaW9uCi0gICBcWyBcXSBsYWJlbCBzZWxlY3QgcG9pbnRzCi0gICBcWyBcXSBmaXggdXAgdGhlIHRpY2sgbWFya3MgYW5kIGxhYmVscwotICAgXFsgXF0gbW92ZSBjb2xvciBsZWdlbmQgdG8gdGhlIHRvcAotICAgXFsgXF0gdGl0bGUsIGxhYmVsIGF4ZXMsIHJlbW92ZSBsZWdlbmQgdGl0bGUKLSAgIFxbIFxdIHRoZW1lIHRoZSBncmFwaCB3aXRoIG5vIHZlcnRpY2FsIGd1aWRlcwotICAgXFsgXF0gYWRkIG1vZGVsIFI8c3VwPjI8L3N1cD4gKGhhcmQpCi0gICBcWyBcXSBhZGQgc291cmNlcyBub3RlIChoYXJkKQotICAgXFsgXF0gZmluYWwgdG91Y2hlcyB0byBtYWtlIGl0IHBlcmZlY3QgKHVzZSBpbWFnZSBlZGl0b3IgZm9yIHRoaXMpCgpBZGRpbmcgdGhlIHRyZW5kIGxpbmUKLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpBZGRpbmcgdGhlIHRyZW5kIGxpbmUgaXMgbm90IHRvbyBkaWZmaWN1bHQsIHRob3VnaCB3ZSBuZWVkIHRvIGd1ZXNzIGF0IHRoZSBtb2RlbCBiZWluZyBkaXNwbHllZCBvbiB0aGUgZ3JhcGguIEEgbGl0dGxlIGJpdCBvZiB0cmlhbCBhbmQgZXJyb3IgbGVkcyB0bwoKYGBgIHtyfQogIChwYzIgPC0gcGMxICsKICAgICBnZW9tX3Ntb290aChhZXMoZ3JvdXAgPSAxKSwKICAgICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLAogICAgICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4gbG9nKHgpLAogICAgICAgICAgICAgICAgIHNlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIikpICsKICAgICBnZW9tX3BvaW50KCkKYGBgCgpOb3RpY2UgdGhhdCB3ZSBwdXQgdGhlIGBnZW9tX2xpbmVgIGxheWVyIGZpcnN0IHNvIHRoYXQgaXQgd2lsbCBiZSBwbG90dGVkIHVuZGVybmVhdGggdGhlIHBvaW50cywgYXMgd2FzIGRvbmUgb24gdGhlIG9yaWdpbmFsIGdyYXBoLgoKVXNlIG9wZW4gcG9pbnRzCi0tLS0tLS0tLS0tLS0tLQoKVGhpcyBvbmUgaXMgYSBsaXR0bGUgdHJpY2t5LiBXZSBrbm93IHRoYXQgd2UgY2FuIGNoYW5nZSB0aGUgc2hhcGUgd2l0aCB0aGUgYHNoYXBlYCBhcmd1bWVudCwgd2hhdCB3aGF0IHZhbHVlIGRvIHdlIHNldCBzaGFwZSB0bz8gVGhlIGV4YW1wbGUgc2hvd24gaW4gYD9zaGFwZWAgY2FuIGhlbHAgdXM6CgpgYGAge3J9CiAgIyMgQSBsb29rIGF0IGFsbCAyNSBzeW1ib2xzCiAgZGYyIDwtIGRhdGEuZnJhbWUoeCA9IDE6NSAsIHkgPSAxOjI1LCB6ID0gMToyNSkKICBzIDwtIGdncGxvdChkZjIsIGFlcyh4ID0geCwgeSA9IHkpKQogIHMgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHopLCBzaXplID0gNCkgKyBzY2FsZV9zaGFwZV9pZGVudGl0eSgpCiAgIyMgV2hpbGUgYWxsIHN5bWJvbHMgaGF2ZSBhIGZvcmVncm91bmQgY29sb3VyLCBzeW1ib2xzIDE5LTI1IGFsc28gdGFrZSBhCiAgIyMgYmFja2dyb3VuZCBjb2xvdXIgKGZpbGwpCiAgcyArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0geiksIHNpemUgPSA0LCBjb2xvdXIgPSAiUmVkIikgKwogICAgc2NhbGVfc2hhcGVfaWRlbnRpdHkoKQogIHMgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHopLCBzaXplID0gNCwgY29sb3VyID0gIlJlZCIsIGZpbGwgPSAiQmxhY2siKSArCiAgICBzY2FsZV9zaGFwZV9pZGVudGl0eSgpCmBgYAoKVGhpcyBzaG93cyB1cyB0aGF0ICpzaGFwZSAxKiBpcyBhbiBvcGVuIGNpcmNsZSwgc28KCmBgYCB7cn0KICBwYzIgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIHNpemUgPSA0KQpgYGAKClRoYXQgaXMgYmV0dGVyLCBidXQgdW5mb3J0dW5hdGVseSB0aGUgc2l6ZSBvZiB0aGUgbGluZSBhcm91bmQgdGhlIHBvaW50cyBpcyBtdWNoIG5hcnJvd2VyIHRoYW4gb24gdGhlIG9yaWdpbmFsLiBUaGlzIGlzIGEgZnJ1c3RyYXRpbmcgYXNwZWN0IG9mIGdncGxvdDIsIGFuZCB3ZSB3aWxsIGhhdmUgdG8gaGFjayBhcm91bmQgaXQuIE9uZSB3YXkgdG8gZG8gdGhhdCBpcyB0byBtdWx0aXBsZSBwb2ludCBsYXllcnMgb2Ygc2xpZ2h0bHkgZGlmZmVyZW50IHNpemVzLgoKYGBgIHtyfQogIChwYzMgPC0gcGMyICsKICAgICBnZW9tX3BvaW50KHNpemUgPSA0LjUsIHNoYXBlID0gMSkgKwogICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIHNoYXBlID0gMSkgKwogICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSwgc2hhcGUgPSAxKSkKYGBgCgpMYWJlbGxpbmcgcG9pbnRzCi0tLS0tLS0tLS0tLS0tLS0KClRoaXMgb25lIGlzIHRyaWNreSBpbiBhIGNvdXBsZSBvZiB3YXlzLiBGaXJzdCwgdGhlcmUgaXMgbm8gYXR0cmlidXRlIGluIHRoZSBkYXRhIHRoYXQgc2VwYXJhdGVzIHBvaW50cyB0aGF0IHNob3VsZCBiZSBsYWJlbGxlZCBmcm9tIHBvaW50cyB0aGF0IHNob3VsZCBub3QgYmUuIFNvIHRoZSBmaXJzdCBzdGVwIGlzIHRvIGlkZW50aWZ5IHRob3NlIHBvaW50cy4KCmBgYCB7cn0KICBwb2ludHNUb0xhYmVsIDwtIGMoIlJ1c3NpYSIsICJWZW5lenVlbGEiLCAiSXJhcSIsICJNeWFubWFyIiwgIlN1ZGFuIiwKICAgICAgICAgICAgICAgICAgICAgIkFmZ2hhbmlzdGFuIiwgIkNvbmdvIiwgIkdyZWVjZSIsICJBcmdlbnRpbmEiLCAiQnJhemlsIiwKICAgICAgICAgICAgICAgICAgICAgIkluZGlhIiwgIkl0YWx5IiwgIkNoaW5hIiwgIlNvdXRoIEFmcmljYSIsICJTcGFuZSIsCiAgICAgICAgICAgICAgICAgICAgICJCb3Rzd2FuYSIsICJDYXBlIFZlcmRlIiwgIkJodXRhbiIsICJSd2FuZGEiLCAiRnJhbmNlIiwKICAgICAgICAgICAgICAgICAgICAgIlVuaXRlZCBTdGF0ZXMiLCAiR2VybWFueSIsICJCcml0YWluIiwgIkJhcmJhZG9zIiwgIk5vcndheSIsICJKYXBhbiIsCiAgICAgICAgICAgICAgICAgICAgICJOZXcgWmVhbGFuZCIsICJTaW5nYXBvcmUiKQpgYGAKCk5vdyB3ZSBjYW4gbGFiZWwgdGhlc2UgcG9pbnRzIHVzaW5nIGBnZW9tX3RleHRgLCBsaWtlIHRoaXM6CgpcIytFTkQ8c3ViPlNSQzwvc3ViPgoKYGBgIHtyfQogIChwYzQgPC0gcGMzICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudHJ5KSwKICAgICAgICAgICAgICBjb2xvciA9ICJncmF5MjAiLAogICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0LCBDb3VudHJ5ICVpbiUgcG9pbnRzVG9MYWJlbCkpKQpgYGAKClRoaXMgbW9yZSBvciBsZXNzIGdldHMgdGhlIGluZm9ybWF0aW9uIGFjcm9zcywgYnV0IHRoZSBsYWJlbHMgb3ZlcmxhcCBpbiBhIG1vc3QgdW5wbGVhc2luZyBmYXNoaW9uLiBXZSBjYW4gdXNlIHRoZSBgZ2dyZXBlbGAgcGFja2FnZSB0byBtYWtlIHRoaW5ncyBiZXR0ZXIsIGJ1dCBpZiB5b3Ugd2FudCBwZXJmZWN0aW9uIHlvdSB3aWxsIHByb2JhYmx5IGhhdmUgdG8gZG8gc29tZSBoYW5kLWFkanVzdG1lbnQuCgpgYGAge3J9CiAgbGlicmFyeSgiZ2dyZXBlbCIpCiAgcGMzICsKICAgIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBDb3VudHJ5KSwKICAgICAgICAgICAgICBjb2xvciA9ICJncmF5MjAiLAogICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0LCBDb3VudHJ5ICVpbiUgcG9pbnRzVG9MYWJlbCksCiAgICAgICAgICAgICAgZm9yY2UgPSAxMCkKYGBgCgpDaGFuZ2UgdGhlIHJlZ2lvbiBsYWJlbHMgYW5kIG9yZGVyCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClRoaW5rZ3MgYXJlIHN0YXJ0aW5nIHRvIGNvbWUgdG9nZXRoZXIuIFRoZXJlIGFyZSBqdXN0IGEgY291cGxlIG1vcmUgdGhpbmdzIHdlIG5lZWQgdG8gYWRkLCBhbmQgdGhlbiBhbGwgdGhhdCB3aWxsIGJlIGxlZnQgYXJlIHRoZW1laW5nIGNoYW5nZXMuCgpDb21wYXJpbmcgb3VyIGdyYXBoIHRvIHRoZSBvcmlnaW5hbCB3ZSBub3RpY2UgdGhhdCB0aGUgbGFiZWxzIGFuZCBvcmRlciBvZiB0aGUgUmVnaW9ucyBpbiB0aGUgY29sb3IgbGVnZW5kIGRpZmZlci4gVG8gY29ycmVjdCB0aGlzIHdlIG5lZWQgdG8gY2hhbmdlIGJvdGggdGhlIGxhYmVscyBhbmQgb3JkZXIgb2YgdGhlIFJlZ2lvbiB2YXJpYWJsZS4gV2UgY2FuIGRvIHRoaXMgd2l0aCB0aGUgYGZhY3RvcmAgZnVuY3Rpb24uCgpgYGAge3J9CiAgZGF0JFJlZ2lvbiA8LSBmYWN0b3IoZGF0JFJlZ2lvbiwKICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJFVSBXLiBFdXJvcGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFtZXJpY2FzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBc2lhIFBhY2lmaWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVhc3QgRVUgQ2VtdCBBc2lhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNRU5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTU0EiKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJPRUNEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBbWVyaWNhcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNpYSAmXG5PY2VhbmlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDZW50cmFsICZcbkVhc3Rlcm4gRXVyb3BlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNaWRkbGUgRWFzdCAmXG5ub3J0aCBBZnJpY2EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN1Yi1TYWhhcmFuXG5BZnJpY2EiKSkKYGBgCgpOb3cgd2hlbiB3ZSBjb25zdHJ1Y3QgdGhlIHBsb3QgdXNpbmcgdGhlc2UgZGF0YSB0aGUgb3JkZXIgc2hvdWxkIGFwcGVhciBhcyBpdCBkb2VzIGluIHRoZSBvcmlnaW5hbC4KCmBgYCB7cn0KICBwYzQkZGF0YSA8LSBkYXQKICBwYzQKYGBgCgpBZGQgdGl0bGUgYW5kIGZvcm1hdCBheGVzCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClRoZSBuZXh0IHN0ZXAgaXMgdG8gYWRkIHRoZSB0aXRsZSBhbmQgZm9ybWF0IHRoZSBheGVzLiBXZSBkbyB0aGF0IHVzaW5nIHRoZSBgc2NhbGVzYCBzeXN0ZW0gaW4gZ2dwbG90Mi4KCmBgYCB7cn0KICBsaWJyYXJ5KGdyaWQpCiAgKHBjNSA8LSBwYzQgKwogICAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiQ29ycnVwdGlvbiBQZXJjZXB0aW9ucyBJbmRleCwgMjAxMSAoMTA9bGVhc3QgY29ycnVwdCkiLAogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoLjksIDEwLjUpLAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IDE6MTApICsKICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkh1bWFuIERldmVsb3BtZW50IEluZGV4LCAyMDExICgxPUJlc3QpIiwKICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAuMiwgMS4wKSwKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMC4yLCAxLjAsIGJ5ID0gMC4xKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCIjMjQ1NzZEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMDk5REQ3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMjhBQURDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMjQ4RTg0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRjI1ODNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjOTY1MDNGIikpICsKICAgIGdndGl0bGUoIkNvcnJ1cHRpb24gYW5kIEh1bWFuIGRldmVsb3BtZW50IikpCmBgYAoKVGhlbWUgdHdlYWtzCi0tLS0tLS0tLS0tLQoKT3VyIGdyYXBoIGlzIGFsbW9zdCB0aGVyZS4gVG8gZmluaXNoIHVwLCB3ZSBuZWVkIHRvIGFkanVzdCBzb21lIG9mIHRoZSB0aGVtZSBlbGVtZW50cywgYW5kIGxhYmVsIHRoZSBheGVzIGFuZCBsZWdlbmRzLiBUaGlzIHBhcnQgdXN1YWxseSBpbnZvbHZlcyBzb21lIHRyaWFsIGFuZCBlcnJvciBhcyB5b3UgZmlndXJlIG91dCB3aGVyZSB0aGluZ3MgbmVlZCB0byBiZSBwb3NpdGlvbmVkLiBUbyBzZWUgd2hhdCB0aGVzZSB2YXJpb3VzIHRoZW1lIHNldHRpbmdzIGRvIHlvdSBjYW4gY2hhbmdlIHRoZW0gYW5kIG9ic2VydmUgdGhlIHJlc3VsdHMuCgpgYGAge3J9CiAgbGlicmFyeShncmlkKSAjIGZvciB0aGUgJ3VuaXQnIGZ1bmN0aW9uCiAgKHBjNiA8LSBwYzUgKwogICAgdGhlbWVfbWluaW1hbCgpICsgIyBzdGFydCB3aXRoIGEgbWluaW1hbCB0aGVtZSBhbmQgYWRkIHdoYXQgd2UgbmVlZAogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmF5MjAiKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoInRvcCIpLCAjIHBvc2l0aW9uIHRoZSBsZWdlbmQgaW4gdGhlIHVwcGVyIGxlZnQgCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAwLjEsICMgYW5jaG9yIHBvaW50IGZvciBsZWdlbmQucG9zaXRpb24uCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGNvbG9yID0gImdyYXkxMCIpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSAtMSksICMgbW92ZSB0aXRsZSBhd2F5IGZyb20gYXhpcwogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gMiksICMgbW92ZSBhd2F5IGZvciBheGlzCiAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksICMgZWxlbWVudF9ibGFuaygpIGlzIGhvdyB3ZSByZW1vdmUgZWxlbWVudHMKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5NDAiLCBzaXplID0gMC41KSwKICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5NTAiLCBzaXplID0gMC41KSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICAgKSkKYGBgCgpBZGQgbW9kZWwgUjxzdXA+Mjwvc3VwPiBhbmQgc291cmNlIG5vdGUKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpUaGUgbGFzdCBiaXQgb2YgaW5mb3JtYXRpb24gdGhhdCB3ZSB3YW50IHRvIGhhdmUgb24gdGhlIGdyYXBoIGlzIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgdGhlIG1vZGVsIHJlcHJlc2VudGVkIGJ5IHRoZSB0cmVuZCBsaW5lLiBMZXRzIGZpdCB0aGF0IG1vZGVsIGFuZCBwdWxsIG91dCB0aGUgUjxzdXA+Mjwvc3VwPiBmaXJzdCwgdGhlbiB0aGluayBhYm91dCBob3cgdG8gZ2V0IGl0IG9udG8gdGhlIGdyYXBoLgoKYGBgIHtyfQogIChtUjIgPC0gc3VtbWFyeShsbShIREkgfiBsb2coQ1BJKSwgZGF0YSA9IGRhdCkpJHIuc3F1YXJlZCkKYGBgCgpPSywgbm93IHRoYXQgd2UndmUgY2FsY3VsYXRlZCB0aGUgdmFsdWVzLCBsZXQncyB0aGluayBhYm91dCBob3cgdG8gZ2V0IHRoZW0gb24gdGhlIGdyYXBoLiBnZ3Bsb3QyIGhhcyBhbiBgYW5ub3RhdGVgIGZ1bmN0aW9uLCBidXQgdGhpcyBpcyBub3QgY29udmVuaWVudCBmb3IgYWRkaW5nIGVsZW1lbnRzIG91dHNpZGUgdGhlIHBsb3QgYXJlYS4gVGhlIGBncmlkYCBwYWNrYWdlIGhhcyBuaWNlIGZ1bmN0aW9ucyBmb3IgZG9pbmcgdGhpcywgc28gd2UnbGwgdXNlIHRob3NlLgoKQW5kIGhlcmUgaXQgaXMsIG91ciBmaW5hbCB2ZXJzaW9uIQoKYGBgIHtyfQogIGxpYnJhcnkoZ3JpZCkKICBwbmcoZmlsZSA9ICJpbWFnZXMvZWNvblNjYXR0ZXIxMC5wbmciLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQogIHBjNiAKICBncmlkLnRleHQoIlNvdXJjZXM6IFRyYW5zcGFyZW5jeSBJbnRlcm5hdGlvbmFsOyBVTiBIdW1hbiBEZXZlbG9wbWVudCBSZXBvcnQiLAogICAgICAgICAgIHggPSAuMDIsIHkgPSAuMDMsCiAgICAgICAgICAganVzdCA9ICJsZWZ0IiwKICAgICAgICAgICBkcmF3ID0gVFJVRSkKICBncmlkLnNlZ21lbnRzKHgwID0gMC44MSwgeDEgPSAwLjgyNSwKICAgICAgICAgICAgICAgIHkwID0gMC45MCwgeTEgPSAwLjkwLAogICAgICAgICAgICAgICAgZ3AgPSBncGFyKGNvbCA9ICJyZWQiKSwKICAgICAgICAgICAgICAgIGRyYXcgPSBUUlVFKQogIGdyaWQudGV4dChwYXN0ZTAoIlLCsiA9ICIsCiAgICAgICAgICAgICAgICAgICBhcy5pbnRlZ2VyKG1SMioxMDApLAogICAgICAgICAgICAgICAgICAgIiUiKSwKICAgICAgICAgICAgeCA9IDAuODM1LCB5ID0gMC45MCwKICAgICAgICAgICAgZ3AgPSBncGFyKGNvbCA9ICJncmF5MjAiKSwKICAgICAgICAgICAgZHJhdyA9IFRSVUUsCiAgICAgICAgICAgIGp1c3QgPSAibGVmdCIpCgogIGRldi5vZmYoKQpgYGAKCiFbXShpbWFnZXMvZWNvblNjYXR0ZXIxMC5wbmcpCgpDb21wYXJpbmcgaXQgdG8gdGhlIG9yaWdpbmFsIHN1Z2dlc3RzIHRoYXQgd2UndmUgZ290IG1vc3Qgb2YgdGhlIGltcG9ydGFudCBlbGVtZW50cywgdGhvdWdoIG9mIGNvdXJzZSB0aGUgdHdvIGdyYXBocyBhcmUgbm90IGlkZW50aWNhbC4gIVtdKGltYWdlcy9FY29ub21pc3QxLnBuZykKCldyYXAtdXAKPT09PT09PQoKSGVscCBVcyBNYWtlIFRoaXMgV29ya3Nob3AgRXZlbiBCZXR0ZXIhCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLSAgIFBsZWFzZSB0YWtlIGEgbW9tZW50IHRvIGZpbGwgb3V0IGEgdmVyeSBzaG9ydCBmZWVkYmFjayBmb3JtCi0gICBUaGVzZSB3b3Jrc2hvcHMgZXhpc3QgZm9yIHlvdSAtLSB0ZWxsIHVzIHdoYXQgeW91IG5lZWQhCi0gICA8aHR0cDovL3Rpbnl1cmwuY29tL1ItZ3JhcGhpY3MtZmVlZGJhY2s+CgpBZGRpdGlvbmFsIHJlc291cmNlcwotLS0tLS0tLS0tLS0tLS0tLS0tLQoKLSAgIGdncGxvdDIgcmVzb3VyY2VzCiAgICAtICAgTWFpbGluZyBsaXN0OiA8aHR0cDovL2dyb3Vwcy5nb29nbGUuY29tL2dyb3VwL2dncGxvdDI+CiAgICAtICAgV2lraTogPGh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvZ2dwbG90Mi93aWtpPgogICAgLSAgIFdlYnNpdGU6IDxodHRwOi8vaGFkLmNvLm56L2dncGxvdDIvPgogICAgLSAgIFN0YWNrT3ZlcmZsb3c6IDxodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9nZ3Bsb3Q+Ci0gICBJUVNTIHJlc291cmNlcwogICAgLSAgIFJlc2VhcmNoIHRlY2hub2xvZ3kgY29uc3VsdGluZzogPGh0dHA6Ly9wcm9qZWN0cy5pcS5oYXJ2YXJkLmVkdS9ydGM+CiAgICAtICAgV29ya3Nob3BzOiA8aHR0cDovL3Byb2plY3RzLmlxLmhhcnZhcmQuZWR1L3J0Yy9maWx0ZXJfYnkvd29ya3Nob3BzPgo=